Compare commits
126 Commits
android-13
...
android-14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efff6adeeb | ||
|
|
d93b530ac8 | ||
|
|
f21340f7aa | ||
|
|
e0c894408a | ||
|
|
257a1c884d | ||
|
|
c100d7e802 | ||
|
|
4ce6762945 | ||
|
|
fe3702223f | ||
|
|
83aa66b17d | ||
|
|
8d7a55be5b | ||
|
|
de58618421 | ||
|
|
e6847c65a8 | ||
|
|
b5bde8451c | ||
|
|
090ea0281c | ||
|
|
bc4818b058 | ||
|
|
2993d3bb49 | ||
|
|
5a182f4e7c | ||
|
|
2d4e7c8264 | ||
|
|
4a278b69b1 | ||
|
|
02a0b41a15 | ||
|
|
093eb075a6 | ||
|
|
0b766e4523 | ||
|
|
453fd47030 | ||
|
|
dda187d300 | ||
|
|
0b8218d8eb | ||
|
|
91c12db070 | ||
|
|
d8f380961e | ||
|
|
b088a448cd | ||
|
|
c4f6c3b00b | ||
|
|
1654b8f9e0 | ||
|
|
14398a1cbb | ||
|
|
cddb28cf26 | ||
|
|
538e137bca | ||
|
|
e69118042f | ||
|
|
a6b8d85b34 | ||
|
|
f3fe362c93 | ||
|
|
eedecaef96 | ||
|
|
e637ec0c38 | ||
|
|
e744c06f61 | ||
|
|
ac522db857 | ||
|
|
aa20311969 | ||
|
|
da14c7b8e4 | ||
|
|
e7878e3cf8 | ||
|
|
cff2d0e19e | ||
|
|
8da5bd27e9 | ||
|
|
0216aa55b4 | ||
|
|
0298c2cc5d | ||
|
|
00dcf69ce8 | ||
|
|
1ea0890a36 | ||
|
|
a77feca086 | ||
|
|
99a8f7fb72 | ||
|
|
8ae26df15c | ||
|
|
82a4a67f6b | ||
|
|
8e93a9a9ef | ||
|
|
23c1f7c72f | ||
|
|
d3ed771f39 | ||
|
|
12fba361bd | ||
|
|
6bcde572dd | ||
|
|
cb004d1ba1 | ||
|
|
20a17607ae | ||
|
|
473caaff5b | ||
|
|
787552f832 | ||
|
|
5f945e2fcd | ||
|
|
c08da2d6ad | ||
|
|
4458920799 | ||
|
|
61fed8a3a6 | ||
|
|
efb3165e3d | ||
|
|
a493ba76b4 | ||
|
|
ae60a5657e | ||
|
|
feb60de5c3 | ||
|
|
c67644f1da | ||
|
|
9343b81afd | ||
|
|
71f53b4218 | ||
|
|
f131b0faeb | ||
|
|
6c64d5aff2 | ||
|
|
de594995da | ||
|
|
4c16a1a26f | ||
|
|
862e66202c | ||
|
|
4055a476aa | ||
|
|
2136a46ab7 | ||
|
|
b9c7e5c2c8 | ||
|
|
d86e88a622 | ||
|
|
7eac28e410 | ||
|
|
ea4c92f734 | ||
|
|
c9cd938dfd | ||
|
|
c9437e5244 | ||
|
|
50bcfa5fb9 | ||
|
|
efc50485b8 | ||
|
|
f1806d237f | ||
|
|
ae57a99d7d | ||
|
|
1d03a0fa75 | ||
|
|
c9038af29e | ||
|
|
f3053920bf | ||
|
|
c7b31d24b9 | ||
|
|
cb3559539a | ||
|
|
71cdfa6ad5 | ||
|
|
8d0d0e1c7a | ||
|
|
4b8b223db2 | ||
|
|
728aca7703 | ||
|
|
a872030a35 | ||
|
|
79e7d7f4ba | ||
|
|
7f62a48ab5 | ||
|
|
b5415b6872 | ||
|
|
b76a1d987f | ||
|
|
ae2130470e | ||
|
|
ac6290bea7 | ||
|
|
4051bbbed7 | ||
|
|
2a7edda70a | ||
|
|
59b6ada7b7 | ||
|
|
9908434c14 | ||
|
|
668a10f9b9 | ||
|
|
fc4b45ebd3 | ||
|
|
1afe6d51ee | ||
|
|
1ae0f0f3f6 | ||
|
|
de0b35b974 | ||
|
|
ae88d01d8d | ||
|
|
d759de9f96 | ||
|
|
89d3e81be8 | ||
|
|
71f264c498 | ||
|
|
26417da5d3 | ||
|
|
b3b458edf9 | ||
|
|
74961d4dfb | ||
|
|
9ffa1801c7 | ||
|
|
4d4fe69223 | ||
|
|
0a75519ab5 | ||
|
|
3062a35eb1 |
@@ -3,4 +3,4 @@
|
||||
|
||||
[codespell]
|
||||
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
||||
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink
|
||||
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nce,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -61,3 +61,6 @@
|
||||
[submodule "simpleini"]
|
||||
path = externals/simpleini
|
||||
url = https://github.com/brofield/simpleini.git
|
||||
[submodule "oaknut"]
|
||||
path = externals/oaknut
|
||||
url = https://github.com/merryhime/oaknut
|
||||
|
||||
@@ -290,6 +290,7 @@ find_package(lz4 REQUIRED)
|
||||
find_package(nlohmann_json 3.8 REQUIRED)
|
||||
find_package(Opus 1.3 MODULE)
|
||||
find_package(RenderDoc MODULE)
|
||||
find_package(SimpleIni MODULE)
|
||||
find_package(stb MODULE)
|
||||
find_package(VulkanMemoryAllocator CONFIG)
|
||||
find_package(ZLIB 1.2 REQUIRED)
|
||||
|
||||
19
CMakeModules/FindSimpleIni.cmake
Normal file
19
CMakeModules/FindSimpleIni.cmake
Normal file
@@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_path(SimpleIni_INCLUDE_DIR SimpleIni.h)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SimpleIni
|
||||
REQUIRED_VARS SimpleIni_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if (SimpleIni_FOUND AND NOT TARGET SimpleIni::SimpleIni)
|
||||
add_library(SimpleIni::SimpleIni INTERFACE IMPORTED)
|
||||
set_target_properties(SimpleIni::SimpleIni PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${SimpleIni_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(SimpleIni_INCLUDE_DIR)
|
||||
@@ -1,7 +1,6 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [11535](https://github.com/yuzu-emu/yuzu//pull/11535) | [`50bcfa5fb`](https://github.com/yuzu-emu/yuzu//pull/11535/files) | renderer_vulkan: Introduce separate cmd buffer for uploads | [GPUCode](https://github.com/GPUCode/) | Yes |
|
||||
| [11889](https://github.com/yuzu-emu/yuzu//pull/11889) | [`a0410ec08`](https://github.com/yuzu-emu/yuzu//pull/11889/files) | configuration: Unify config handling across frontends | [t895](https://github.com/t895/) | Yes |
|
||||
| [12074](https://github.com/yuzu-emu/yuzu//pull/12074) | [`15f35b865`](https://github.com/yuzu-emu/yuzu//pull/12074/files) | Implement Native Code Execution (NCE) | [GPUCode](https://github.com/GPUCode/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
5
dist/languages/.tx/config
vendored
5
dist/languages/.tx/config
vendored
@@ -6,3 +6,8 @@ file_filter = <lang>.ts
|
||||
source_file = en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
|
||||
[o:yuzu-emulator:p:yuzu:r:yuzu-android]
|
||||
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
|
||||
source_file = ../../src/android/app/src/main/res/values/strings.xml
|
||||
type = ANDROID
|
||||
|
||||
8
externals/CMakeLists.txt
vendored
8
externals/CMakeLists.txt
vendored
@@ -20,6 +20,10 @@ if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak)
|
||||
endif()
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_arm64 AND NOT TARGET merry::oaknut)
|
||||
add_subdirectory(oaknut)
|
||||
endif()
|
||||
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic)
|
||||
set(DYNARMIC_IGNORE_ASSERTS ON)
|
||||
add_subdirectory(dynarmic)
|
||||
@@ -292,4 +296,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||
endif()
|
||||
|
||||
# SimpleIni
|
||||
add_subdirectory(simpleini)
|
||||
if (NOT TARGET SimpleIni::SimpleIni)
|
||||
add_subdirectory(simpleini)
|
||||
endif()
|
||||
|
||||
1
externals/oaknut
vendored
Submodule
1
externals/oaknut
vendored
Submodule
Submodule externals/oaknut added at 918bd94f02
@@ -299,6 +299,11 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun getPerfStats(): DoubleArray
|
||||
|
||||
/**
|
||||
* Returns the current CPU backend.
|
||||
*/
|
||||
external fun getCpuBackend(): String
|
||||
|
||||
/**
|
||||
* Notifies the core emulation that the orientation has changed.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ enum class IntSetting(
|
||||
override val category: Settings.Category,
|
||||
override val androidDefault: Int? = null
|
||||
) : AbstractIntSetting {
|
||||
CPU_BACKEND("cpu_backend", Settings.Category.Cpu),
|
||||
CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
|
||||
REGION_INDEX("region_index", Settings.Category.System),
|
||||
LANGUAGE_INDEX("language_index", Settings.Category.System),
|
||||
|
||||
@@ -73,10 +73,19 @@ abstract class SettingsItem(
|
||||
R.string.frame_limit_slider,
|
||||
R.string.frame_limit_slider_description,
|
||||
1,
|
||||
200,
|
||||
400,
|
||||
"%"
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.CPU_BACKEND,
|
||||
R.string.cpu_backend,
|
||||
0,
|
||||
R.array.cpuBackendArm64Names,
|
||||
R.array.cpuBackendArm64Values
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.CPU_ACCURACY,
|
||||
|
||||
@@ -269,6 +269,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_DEBUG.key)
|
||||
|
||||
add(HeaderSetting(R.string.cpu))
|
||||
add(IntSetting.CPU_BACKEND.key)
|
||||
add(IntSetting.CPU_ACCURACY.key)
|
||||
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
||||
add(SettingsItem.FASTMEM_COMBINED)
|
||||
|
||||
@@ -414,8 +414,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
perfStatsUpdater = {
|
||||
if (emulationViewModel.emulationStarted.value) {
|
||||
val perfStats = NativeLibrary.getPerfStats()
|
||||
val cpuBackend = NativeLibrary.getCpuBackend()
|
||||
if (_binding != null) {
|
||||
binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
|
||||
binding.showFpsText.text =
|
||||
String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
|
||||
}
|
||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.Installable
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
class InstallableFragment : Fragment() {
|
||||
private var _binding: FragmentInstallablesBinding? = null
|
||||
@@ -78,7 +80,15 @@ class InstallableFragment : Fragment() {
|
||||
R.string.manage_save_data,
|
||||
R.string.import_export_saves_description,
|
||||
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
|
||||
export = { mainActivity.exportSave() }
|
||||
export = {
|
||||
mainActivity.exportSaves.launch(
|
||||
"yuzu saves - ${
|
||||
LocalDateTime.now().format(
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
||||
)
|
||||
}.zip"
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Installable(
|
||||
|
||||
@@ -18,8 +18,8 @@ class Game(
|
||||
val version: String = "",
|
||||
val isHomebrew: Boolean = false
|
||||
) : Parcelable {
|
||||
val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
|
||||
val keyLastPlayedTime get() = "${programId}_LastPlayed"
|
||||
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
|
||||
val keyLastPlayedTime get() = "${path}_LastPlayed"
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Game) {
|
||||
|
||||
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.DocumentsContract
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.view.WindowManager
|
||||
@@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
@@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||
@@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||
import org.yuzu.yuzu_emu.utils.*
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
@@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
// Get first subfolder in saves folder (should be the user folder)
|
||||
val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
|
||||
private var lastZipCreated: File? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val splashScreen = installSplashScreen()
|
||||
@@ -658,75 +651,31 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
/**
|
||||
* Zips the save files located in the given folder path and creates a new zip file with the current date and time.
|
||||
* @return true if the zip file is successfully created, false otherwise.
|
||||
*/
|
||||
private fun zipSave(): Boolean {
|
||||
try {
|
||||
val tempFolder = File(getPublicFilesDir().canonicalPath, "temp")
|
||||
tempFolder.mkdirs()
|
||||
val saveFolder = File(savesFolderRoot)
|
||||
val outputZipFile = File(
|
||||
tempFolder,
|
||||
"yuzu saves - ${
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
}.zip"
|
||||
)
|
||||
outputZipFile.createNewFile()
|
||||
val result = FileUtil.zipFromInternalStorage(
|
||||
saveFolder,
|
||||
savesFolderRoot,
|
||||
BufferedOutputStream(FileOutputStream(outputZipFile))
|
||||
)
|
||||
if (result == TaskState.Failed) {
|
||||
return false
|
||||
}
|
||||
lastZipCreated = outputZipFile
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
|
||||
*/
|
||||
fun exportSave() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val wasZipCreated = zipSave()
|
||||
val lastZipFile = lastZipCreated
|
||||
if (!wasZipCreated || lastZipFile == null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
getString(R.string.export_save_failed),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
val file = DocumentFile.fromSingleUri(
|
||||
this@MainActivity,
|
||||
DocumentsContract.buildDocumentUri(
|
||||
DocumentProvider.AUTHORITY,
|
||||
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
|
||||
)
|
||||
)!!
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
.setDataAndType(file.uri, "application/zip")
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.putExtra(Intent.EXTRA_STREAM, file.uri)
|
||||
startForResultExportSave.launch(
|
||||
Intent.createChooser(
|
||||
intent,
|
||||
getString(R.string.share_save_file)
|
||||
)
|
||||
)
|
||||
}
|
||||
val exportSaves = registerForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/zip")
|
||||
) { result ->
|
||||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.save_files_exporting,
|
||||
false
|
||||
) {
|
||||
val zipResult = FileUtil.zipFromInternalStorage(
|
||||
File(savesFolderRoot),
|
||||
savesFolderRoot,
|
||||
BufferedOutputStream(contentResolver.openOutputStream(result))
|
||||
)
|
||||
return@newInstance when (zipResult) {
|
||||
TaskState.Completed -> getString(R.string.export_success)
|
||||
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
||||
}
|
||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private val startForResultExportSave =
|
||||
|
||||
@@ -27,6 +27,8 @@ object InputHandler {
|
||||
0x054C -> getInputDS5ButtonKey(event.keyCode)
|
||||
0x057E -> getInputJoyconButtonKey(event.keyCode)
|
||||
0x1532 -> getInputRazerButtonKey(event.keyCode)
|
||||
0x3537 -> getInputRedmagicButtonKey(event.keyCode)
|
||||
0x358A -> getInputBackboneLabsButtonKey(event.keyCode)
|
||||
else -> getInputGenericButtonKey(event.keyCode)
|
||||
}
|
||||
|
||||
@@ -227,6 +229,42 @@ object InputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInputRedmagicButtonKey(key: Int): Int {
|
||||
return when (key) {
|
||||
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
|
||||
KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
|
||||
KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
|
||||
KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
|
||||
KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
|
||||
KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
|
||||
KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
|
||||
KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
|
||||
KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInputBackboneLabsButtonKey(key: Int): Int {
|
||||
return when (key) {
|
||||
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
|
||||
KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
|
||||
KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
|
||||
KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
|
||||
KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
|
||||
KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
|
||||
KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
|
||||
KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
|
||||
KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInputGenericButtonKey(key: Int): Int {
|
||||
return when (key) {
|
||||
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
|
||||
|
||||
@@ -694,6 +694,14 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
|
||||
return j_stats;
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
|
||||
if (Settings::IsNceEnabled()) {
|
||||
return ToJString(env, "NCE");
|
||||
}
|
||||
|
||||
return ToJString(env, "JIT");
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
|
||||
jclass clazz,
|
||||
jstring j_path) {}
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingVertical="4dp"
|
||||
app:checkedChip="@id/chip_recently_played"
|
||||
app:chipSpacingHorizontal="12dp"
|
||||
app:singleLine="true"
|
||||
app:singleSelection="true">
|
||||
|
||||
@@ -175,6 +175,24 @@
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="cpuBackendArm64Names">
|
||||
<item>@string/cpu_backend_dynarmic</item>
|
||||
<item>@string/cpu_backend_nce</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="cpuBackendArm64Values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="cpuBackendX86Names">
|
||||
<item>@string/cpu_backend_dynarmic</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="cpuBackendX86Values">
|
||||
<item>0</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="cpuAccuracyNames">
|
||||
<item>@string/auto</item>
|
||||
<item>@string/cpu_accuracy_accurate</item>
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
<string name="manage_save_data">Manage save data</string>
|
||||
<string name="manage_save_data_description">Save data found. Please select an option below.</string>
|
||||
<string name="import_export_saves_description">Import or export save files</string>
|
||||
<string name="save_files_exporting">Exporting save files…</string>
|
||||
<string name="save_file_imported_success">Imported successfully</string>
|
||||
<string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
|
||||
<string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
|
||||
@@ -184,6 +185,7 @@
|
||||
<string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string>
|
||||
<string name="frame_limit_slider">Limit speed percent</string>
|
||||
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
|
||||
<string name="cpu_backend">CPU backend</string>
|
||||
<string name="cpu_accuracy">CPU accuracy</string>
|
||||
<string name="value_with_units">%1$s%2$s</string>
|
||||
|
||||
@@ -256,6 +258,7 @@
|
||||
<string name="cancelling">Cancelling</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="export_success">Exported successfully</string>
|
||||
|
||||
<!-- GPU driver installation -->
|
||||
<string name="select_gpu_driver">Select GPU driver</string>
|
||||
@@ -414,6 +417,10 @@
|
||||
<string name="ratio_force_sixteen_ten">Force 16:10</string>
|
||||
<string name="ratio_stretch">Stretch to window</string>
|
||||
|
||||
<!-- CPU Backend -->
|
||||
<string name="cpu_backend_dynarmic">Dynarmic (Slow)</string>
|
||||
<string name="cpu_backend_nce">Native code execution (NCE)</string>
|
||||
|
||||
<!-- CPU Accuracy -->
|
||||
<string name="cpu_accuracy_accurate">Accurate</string>
|
||||
<string name="cpu_accuracy_unsafe">Unsafe</string>
|
||||
|
||||
@@ -12,7 +12,7 @@ bool IsValidChannelCount(u32 channel_count) {
|
||||
}
|
||||
|
||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
return total_stream_count > 0 && stereo_stream_count > 0 &&
|
||||
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -148,7 +148,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out
|
||||
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
|
||||
OpusPacketHeader header{ReverseHeader(*header_p)};
|
||||
|
||||
LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
|
||||
LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
|
||||
header.size, input_data.size_bytes(), in_data.size_bytes());
|
||||
|
||||
R_UNLESS(in_data.size_bytes() >= header.size &&
|
||||
|
||||
@@ -52,6 +52,7 @@ add_library(common STATIC
|
||||
fiber.cpp
|
||||
fiber.h
|
||||
fixed_point.h
|
||||
free_region_manager.h
|
||||
fs/file.cpp
|
||||
fs/file.h
|
||||
fs/fs.cpp
|
||||
@@ -166,6 +167,13 @@ if (WIN32)
|
||||
target_link_libraries(common PRIVATE ntdll)
|
||||
endif()
|
||||
|
||||
if (NOT WIN32)
|
||||
target_sources(common PRIVATE
|
||||
signal_chain.cpp
|
||||
signal_chain.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(common
|
||||
PRIVATE
|
||||
|
||||
55
src/common/free_region_manager.h
Normal file
55
src/common/free_region_manager.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
|
||||
namespace Common {
|
||||
|
||||
class FreeRegionManager {
|
||||
public:
|
||||
explicit FreeRegionManager() = default;
|
||||
~FreeRegionManager() = default;
|
||||
|
||||
void SetAddressSpace(void* start, size_t size) {
|
||||
this->FreeBlock(start, size);
|
||||
}
|
||||
|
||||
std::pair<void*, size_t> FreeBlock(void* block_ptr, size_t size) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
// Check to see if we are adjacent to any regions.
|
||||
auto start_address = reinterpret_cast<uintptr_t>(block_ptr);
|
||||
auto end_address = start_address + size;
|
||||
auto it = m_free_regions.find({start_address - 1, end_address + 1});
|
||||
|
||||
// If we are, join with them, ensuring we stay in bounds.
|
||||
if (it != m_free_regions.end()) {
|
||||
start_address = std::min(start_address, it->lower());
|
||||
end_address = std::max(end_address, it->upper());
|
||||
}
|
||||
|
||||
// Free the relevant region.
|
||||
m_free_regions.insert({start_address, end_address});
|
||||
|
||||
// Return the adjusted pointers.
|
||||
block_ptr = reinterpret_cast<void*>(start_address);
|
||||
size = end_address - start_address;
|
||||
return {block_ptr, size};
|
||||
}
|
||||
|
||||
void AllocateBlock(void* block_ptr, size_t size) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
auto address = reinterpret_cast<uintptr_t>(block_ptr);
|
||||
m_free_regions.subtract({address, address + size});
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_mutex;
|
||||
boost::icl::interval_set<uintptr_t> m_free_regions;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -21,15 +21,18 @@
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/random.h>
|
||||
#include <unistd.h>
|
||||
#include "common/scope_exit.h"
|
||||
|
||||
#endif // ^^^ Linux ^^^
|
||||
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/free_region_manager.h"
|
||||
#include "common/host_memory.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
@@ -141,7 +144,7 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
std::unique_lock lock{placeholder_mutex};
|
||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||
Split(virtual_offset, length);
|
||||
@@ -160,7 +163,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
|
||||
DWORD new_flags{};
|
||||
if (read && write) {
|
||||
new_flags = PAGE_READWRITE;
|
||||
@@ -186,6 +189,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void EnableDirectMappedAddress() {
|
||||
// TODO
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||
|
||||
@@ -353,6 +361,55 @@ private:
|
||||
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
constexpr uintptr_t Map39BitSize = (1ULL << 39);
|
||||
constexpr uintptr_t Map36BitSize = (1ULL << 36);
|
||||
|
||||
// This is not a cryptographic application, we just want something random.
|
||||
std::mt19937_64 rng;
|
||||
|
||||
// We want to ensure we are allocating at an address aligned to the L2 block size.
|
||||
// For Qualcomm devices, we must also allocate memory above 36 bits.
|
||||
const size_t lower = Map36BitSize / HugePageSize;
|
||||
const size_t upper = (Map39BitSize - virtual_size) / HugePageSize;
|
||||
const size_t range = upper - lower;
|
||||
|
||||
// Try up to 64 times to allocate memory at random addresses in the range.
|
||||
for (int i = 0; i < 64; i++) {
|
||||
// Calculate a possible location.
|
||||
uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize;
|
||||
|
||||
// Try to map.
|
||||
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
||||
void* map_pointer =
|
||||
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
|
||||
// If we successfully mapped, we're done.
|
||||
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
||||
return map_pointer;
|
||||
}
|
||||
|
||||
// Unmap if necessary, and try again.
|
||||
if (map_pointer != MAP_FAILED) {
|
||||
munmap(map_pointer, virtual_size);
|
||||
}
|
||||
}
|
||||
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class HostMemory::Impl {
|
||||
public:
|
||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||
@@ -415,8 +472,7 @@ public:
|
||||
}
|
||||
}
|
||||
#else
|
||||
virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
|
||||
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
||||
if (virtual_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
@@ -424,7 +480,7 @@ public:
|
||||
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
|
||||
#endif
|
||||
|
||||
placeholders.add({0, virtual_size});
|
||||
free_manager.SetAddressSpace(virtual_base, virtual_size);
|
||||
good = true;
|
||||
}
|
||||
|
||||
@@ -432,14 +488,29 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
{
|
||||
std::scoped_lock lock{placeholder_mutex};
|
||||
placeholders.subtract({virtual_offset, virtual_offset + length});
|
||||
}
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
// Intersect the range with our address space.
|
||||
AdjustMap(&virtual_offset, &length);
|
||||
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_FIXED, fd, host_offset);
|
||||
// We are removing a placeholder.
|
||||
free_manager.AllocateBlock(virtual_base + virtual_offset, length);
|
||||
|
||||
// Deduce mapping protection flags.
|
||||
int flags = PROT_NONE;
|
||||
if (True(perms & MemoryPermission::Read)) {
|
||||
flags |= PROT_READ;
|
||||
}
|
||||
if (True(perms & MemoryPermission::Write)) {
|
||||
flags |= PROT_WRITE;
|
||||
}
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
if (True(perms & MemoryPermission::Execute)) {
|
||||
flags |= PROT_EXEC;
|
||||
}
|
||||
#endif
|
||||
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd,
|
||||
host_offset);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
@@ -447,47 +518,54 @@ public:
|
||||
// The method name is wrong. We're still talking about the virtual range.
|
||||
// We don't want to unmap, we want to reserve this memory.
|
||||
|
||||
{
|
||||
std::scoped_lock lock{placeholder_mutex};
|
||||
auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1});
|
||||
// Intersect the range with our address space.
|
||||
AdjustMap(&virtual_offset, &length);
|
||||
|
||||
if (it != placeholders.end()) {
|
||||
size_t prev_upper = virtual_offset + length;
|
||||
virtual_offset = std::min(virtual_offset, it->lower());
|
||||
length = std::max(it->upper(), prev_upper) - virtual_offset;
|
||||
}
|
||||
// Merge with any adjacent placeholder mappings.
|
||||
auto [merged_pointer, merged_size] =
|
||||
free_manager.FreeBlock(virtual_base + virtual_offset, length);
|
||||
|
||||
placeholders.add({virtual_offset, virtual_offset + length});
|
||||
}
|
||||
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE,
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
|
||||
int flags = 0;
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
|
||||
// Intersect the range with our address space.
|
||||
AdjustMap(&virtual_offset, &length);
|
||||
|
||||
int flags = PROT_NONE;
|
||||
if (read) {
|
||||
flags |= PROT_READ;
|
||||
}
|
||||
if (write) {
|
||||
flags |= PROT_WRITE;
|
||||
}
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
if (execute) {
|
||||
flags |= PROT_EXEC;
|
||||
}
|
||||
#endif
|
||||
int ret = mprotect(virtual_base + virtual_offset, length, flags);
|
||||
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void EnableDirectMappedAddress() {
|
||||
virtual_base = nullptr;
|
||||
}
|
||||
|
||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||
|
||||
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||
u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||
|
||||
private:
|
||||
/// Release all resources in the object
|
||||
void Release() {
|
||||
if (virtual_base != MAP_FAILED) {
|
||||
int ret = munmap(virtual_base, virtual_size);
|
||||
if (virtual_map_base != MAP_FAILED) {
|
||||
int ret = munmap(virtual_map_base, virtual_size);
|
||||
ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
@@ -502,10 +580,29 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
||||
void AdjustMap(size_t* virtual_offset, size_t* length) {
|
||||
if (virtual_base != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders
|
||||
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
||||
// If we are direct mapped, we want to make sure we are operating on a region
|
||||
// that is in range of our virtual mapping.
|
||||
size_t intended_start = *virtual_offset;
|
||||
size_t intended_end = intended_start + *length;
|
||||
size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base);
|
||||
size_t address_space_end = address_space_start + virtual_size;
|
||||
|
||||
if (address_space_start > intended_end || intended_start > address_space_end) {
|
||||
*virtual_offset = 0;
|
||||
*length = 0;
|
||||
} else {
|
||||
*virtual_offset = std::max(intended_start, address_space_start);
|
||||
*length = std::min(intended_end, address_space_end) - *virtual_offset;
|
||||
}
|
||||
}
|
||||
|
||||
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
||||
FreeRegionManager free_manager{};
|
||||
};
|
||||
|
||||
#else // ^^^ Linux ^^^ vvv Generic vvv
|
||||
@@ -518,11 +615,13 @@ public:
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length) {}
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {}
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
|
||||
|
||||
void EnableDirectMappedAddress() {}
|
||||
|
||||
u8* backing_base{nullptr};
|
||||
u8* virtual_base{nullptr};
|
||||
@@ -535,15 +634,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
try {
|
||||
// Try to allocate a fastmem arena.
|
||||
// The implementation will fail with std::bad_alloc on errors.
|
||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
|
||||
AlignUp(virtual_size, PageAlignment) +
|
||||
3 * HugePageSize);
|
||||
impl =
|
||||
std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
|
||||
AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||
backing_base = impl->backing_base;
|
||||
virtual_base = impl->virtual_base;
|
||||
|
||||
if (virtual_base) {
|
||||
virtual_base += 2 * HugePageSize - 1;
|
||||
virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
|
||||
// Ensure the virtual base is aligned to the L2 block size.
|
||||
virtual_base = reinterpret_cast<u8*>(
|
||||
Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize));
|
||||
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||
}
|
||||
|
||||
@@ -562,7 +662,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
|
||||
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
|
||||
|
||||
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
MemoryPermission perms) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(host_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
@@ -571,7 +672,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
impl->Map(virtual_offset + virtual_base_offset, host_offset, length);
|
||||
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
|
||||
}
|
||||
|
||||
void HostMemory::Unmap(size_t virtual_offset, size_t length) {
|
||||
@@ -584,14 +685,22 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
|
||||
impl->Unmap(virtual_offset + virtual_base_offset, length);
|
||||
}
|
||||
|
||||
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) {
|
||||
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
|
||||
bool execute) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
impl->Protect(virtual_offset + virtual_base_offset, length, read, write);
|
||||
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
|
||||
}
|
||||
|
||||
void HostMemory::EnableDirectMappedAddress() {
|
||||
if (impl) {
|
||||
impl->EnableDirectMappedAddress();
|
||||
virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -4,11 +4,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class MemoryPermission : u32 {
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
ReadWrite = Read | Write,
|
||||
Execute = 1 << 2,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
|
||||
|
||||
/**
|
||||
* A low level linear memory buffer, which supports multiple mappings
|
||||
* Its purpose is to rebuild a given sparse memory layout, including mirrors.
|
||||
@@ -31,11 +40,13 @@ public:
|
||||
HostMemory(HostMemory&& other) noexcept;
|
||||
HostMemory& operator=(HostMemory&& other) noexcept;
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length);
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms);
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length);
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write);
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false);
|
||||
|
||||
void EnableDirectMappedAddress();
|
||||
|
||||
[[nodiscard]] u8* BackingBasePointer() noexcept {
|
||||
return backing_base;
|
||||
|
||||
@@ -41,6 +41,7 @@ SWITCHABLE(AspectRatio, true);
|
||||
SWITCHABLE(AstcDecodeMode, true);
|
||||
SWITCHABLE(AstcRecompression, true);
|
||||
SWITCHABLE(AudioMode, true);
|
||||
SWITCHABLE(CpuBackend, true);
|
||||
SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
@@ -155,6 +156,22 @@ bool IsFastmemEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_nce_enabled = false;
|
||||
|
||||
void SetNceEnabled(bool is_39bit) {
|
||||
const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce;
|
||||
is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit;
|
||||
if (is_nce_selected && !is_nce_enabled) {
|
||||
LOG_WARNING(
|
||||
Common,
|
||||
"Program does not utilize 39-bit address space, unable to natively execute code");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsNceEnabled() {
|
||||
return is_nce_enabled;
|
||||
}
|
||||
|
||||
bool IsDockedMode() {
|
||||
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ SWITCHABLE(AspectRatio, true);
|
||||
SWITCHABLE(AstcDecodeMode, true);
|
||||
SWITCHABLE(AstcRecompression, true);
|
||||
SWITCHABLE(AudioMode, true);
|
||||
SWITCHABLE(CpuBackend, true);
|
||||
SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
@@ -179,6 +180,14 @@ struct Values {
|
||||
&use_speed_limit};
|
||||
|
||||
// Cpu
|
||||
SwitchableSetting<CpuBackend, true> cpu_backend{
|
||||
linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic,
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
CpuBackend::Nce,
|
||||
#else
|
||||
CpuBackend::Dynarmic,
|
||||
#endif
|
||||
"cpu_backend", Category::Cpu};
|
||||
SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
|
||||
CpuAccuracy::Auto, CpuAccuracy::Paranoid,
|
||||
"cpu_accuracy", Category::Cpu};
|
||||
@@ -566,6 +575,8 @@ bool IsGPULevelExtreme();
|
||||
bool IsGPULevelHigh();
|
||||
|
||||
bool IsFastmemEnabled();
|
||||
void SetNceEnabled(bool is_64bit);
|
||||
bool IsNceEnabled();
|
||||
|
||||
bool IsDockedMode();
|
||||
|
||||
|
||||
@@ -129,6 +129,8 @@ ENUM(ShaderBackend, Glsl, Glasm, SpirV);
|
||||
|
||||
ENUM(GpuAccuracy, Normal, High, Extreme);
|
||||
|
||||
ENUM(CpuBackend, Dynarmic, Nce);
|
||||
|
||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
||||
|
||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
namespace Settings {
|
||||
namespace NativeButton {
|
||||
const std::array<const char*, NumButtons> mapping = {{
|
||||
"button_a", "button_b", "button_x", "button_y", "button_lstick",
|
||||
"button_rstick", "button_l", "button_r", "button_zl", "button_zr",
|
||||
"button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
|
||||
"button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
|
||||
"button_a", "button_b", "button_x", "button_y", "button_lstick",
|
||||
"button_rstick", "button_l", "button_r", "button_zl", "button_zr",
|
||||
"button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
|
||||
"button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot",
|
||||
"button_slright", "button_srright",
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,15 @@ enum Values : int {
|
||||
DRight,
|
||||
DDown,
|
||||
|
||||
SL,
|
||||
SR,
|
||||
SLLeft,
|
||||
SRLeft,
|
||||
|
||||
Home,
|
||||
Screenshot,
|
||||
|
||||
SLRight,
|
||||
SRRight,
|
||||
|
||||
NumButtons,
|
||||
};
|
||||
|
||||
|
||||
42
src/common/signal_chain.cpp
Normal file
42
src/common/signal_chain.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/signal_chain.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
T* LookupLibcSymbol(const char* name) {
|
||||
#if defined(__BIONIC__)
|
||||
Common::DynamicLibrary provider("libc.so");
|
||||
if (!provider.IsOpen()) {
|
||||
UNREACHABLE_MSG("Failed to open libc!");
|
||||
}
|
||||
#else
|
||||
// For other operating environments, we assume the symbol is not overridden.
|
||||
const char* base = nullptr;
|
||||
Common::DynamicLibrary provider(base);
|
||||
#endif
|
||||
|
||||
void* sym = provider.GetSymbolAddress(name);
|
||||
if (sym == nullptr) {
|
||||
sym = dlsym(RTLD_DEFAULT, name);
|
||||
}
|
||||
if (sym == nullptr) {
|
||||
UNREACHABLE_MSG("Unable to find symbol {}!", name);
|
||||
}
|
||||
|
||||
return reinterpret_cast<T*>(sym);
|
||||
}
|
||||
|
||||
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) {
|
||||
static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction");
|
||||
return libc_sigaction(signum, act, oldact);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
19
src/common/signal_chain.h
Normal file
19
src/common/signal_chain.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Android's ART overrides sigaction with its own wrapper. This is problematic for SIGSEGV
|
||||
// in particular, because ART's handler accesses tpidr_el0, which conflicts with NCE.
|
||||
// This extracts the libc symbol and calls it directly.
|
||||
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif
|
||||
@@ -521,17 +521,28 @@ add_library(core STATIC
|
||||
hle/service/grc/grc.h
|
||||
hle/service/hid/hid.cpp
|
||||
hle/service/hid/hid.h
|
||||
hle/service/hid/hid_debug_server.cpp
|
||||
hle/service/hid/hid_debug_server.h
|
||||
hle/service/hid/hid_firmware_settings.cpp
|
||||
hle/service/hid/hid_firmware_settings.h
|
||||
hle/service/hid/hid_server.cpp
|
||||
hle/service/hid/hid_server.h
|
||||
hle/service/hid/hid_system_server.cpp
|
||||
hle/service/hid/hid_system_server.h
|
||||
hle/service/hid/hid_util.h
|
||||
hle/service/hid/hidbus.cpp
|
||||
hle/service/hid/hidbus.h
|
||||
hle/service/hid/irs.cpp
|
||||
hle/service/hid/irs.h
|
||||
hle/service/hid/irs_ring_lifo.h
|
||||
hle/service/hid/resource_manager.cpp
|
||||
hle/service/hid/resource_manager.h
|
||||
hle/service/hid/ring_lifo.h
|
||||
hle/service/hid/xcd.cpp
|
||||
hle/service/hid/xcd.h
|
||||
hle/service/hid/errors.h
|
||||
hle/service/hid/controllers/console_sixaxis.cpp
|
||||
hle/service/hid/controllers/console_sixaxis.h
|
||||
hle/service/hid/controllers/console_six_axis.cpp
|
||||
hle/service/hid/controllers/console_six_axis.h
|
||||
hle/service/hid/controllers/controller_base.cpp
|
||||
hle/service/hid/controllers/controller_base.h
|
||||
hle/service/hid/controllers/debug_pad.cpp
|
||||
@@ -546,6 +557,10 @@ add_library(core STATIC
|
||||
hle/service/hid/controllers/npad.h
|
||||
hle/service/hid/controllers/palma.cpp
|
||||
hle/service/hid/controllers/palma.h
|
||||
hle/service/hid/controllers/seven_six_axis.cpp
|
||||
hle/service/hid/controllers/seven_six_axis.h
|
||||
hle/service/hid/controllers/six_axis.cpp
|
||||
hle/service/hid/controllers/six_axis.h
|
||||
hle/service/hid/controllers/stubbed.cpp
|
||||
hle/service/hid/controllers/stubbed.h
|
||||
hle/service/hid/controllers/touchscreen.cpp
|
||||
@@ -715,6 +730,7 @@ add_library(core STATIC
|
||||
hle/service/nvnflinger/producer_listener.h
|
||||
hle/service/nvnflinger/status.h
|
||||
hle/service/nvnflinger/ui/fence.h
|
||||
hle/service/nvnflinger/ui/graphic_buffer.cpp
|
||||
hle/service/nvnflinger/ui/graphic_buffer.h
|
||||
hle/service/nvnflinger/window.h
|
||||
hle/service/olsc/olsc.cpp
|
||||
@@ -910,6 +926,23 @@ if (ENABLE_WEB_SERVICE)
|
||||
target_link_libraries(core PRIVATE web_service)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_arm64 AND (ANDROID OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
|
||||
target_compile_definitions(core PRIVATE -DHAS_NCE)
|
||||
enable_language(C ASM)
|
||||
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
|
||||
|
||||
target_sources(core PRIVATE
|
||||
arm/nce/arm_nce.cpp
|
||||
arm/nce/arm_nce.h
|
||||
arm/nce/arm_nce.s
|
||||
arm/nce/guest_context.h
|
||||
arm/nce/patch.cpp
|
||||
arm/nce/patch.h
|
||||
arm/nce/instructions.h
|
||||
)
|
||||
target_link_libraries(core PRIVATE merry::oaknut)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
|
||||
@@ -201,6 +201,8 @@ void ARM_Interface::Run() {
|
||||
if (True(hr & HaltReason::DataAbort)) {
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
|
||||
} else {
|
||||
LogBacktrace();
|
||||
}
|
||||
current_thread->RequestSuspend(SuspendType::Debug);
|
||||
break;
|
||||
|
||||
@@ -81,6 +81,9 @@ public:
|
||||
// thread context to be 800 bytes in size.
|
||||
static_assert(sizeof(ThreadContext64) == 0x320);
|
||||
|
||||
/// Perform any backend-specific initialization.
|
||||
virtual void Initialize() {}
|
||||
|
||||
/// Runs the CPU until an event happens
|
||||
void Run();
|
||||
|
||||
|
||||
400
src/core/arm/nce/arm_nce.cpp
Normal file
400
src/core/arm/nce/arm_nce.cpp
Normal file
@@ -0,0 +1,400 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
#include "common/signal_chain.h"
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
#include "core/arm/nce/patch.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
struct sigaction g_orig_action;
|
||||
|
||||
// Verify assembly offsets.
|
||||
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
|
||||
static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0NativeContext);
|
||||
static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
|
||||
static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic);
|
||||
|
||||
fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
|
||||
while (header->magic != FPSIMD_MAGIC) {
|
||||
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
|
||||
}
|
||||
return reinterpret_cast<fpsimd_context*>(header);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void* ARM_NCE::RestoreGuestContext(void* raw_context) {
|
||||
// Retrieve the host context.
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
|
||||
// Thread-local parameters will be located in x9.
|
||||
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]);
|
||||
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
|
||||
|
||||
// Retrieve the host floating point state.
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
|
||||
// Save host callee-saved registers.
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8],
|
||||
sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19],
|
||||
sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
|
||||
// Save stack pointer.
|
||||
guest_ctx->host_ctx.host_sp = host_ctx.sp;
|
||||
|
||||
// Restore all guest state except tpidr_el0.
|
||||
host_ctx.sp = guest_ctx->sp;
|
||||
host_ctx.pc = guest_ctx->pc;
|
||||
host_ctx.pstate = guest_ctx->pstate;
|
||||
fpctx->fpcr = guest_ctx->fpcr;
|
||||
fpctx->fpsr = guest_ctx->fpsr;
|
||||
std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs));
|
||||
std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs));
|
||||
|
||||
// Return the new thread-local storage pointer.
|
||||
return tpidr;
|
||||
}
|
||||
|
||||
void ARM_NCE::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
|
||||
// Retrieve the host context.
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
|
||||
// Retrieve the host floating point state.
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
|
||||
// Save all guest registers except tpidr_el0.
|
||||
std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs));
|
||||
std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs));
|
||||
guest_ctx->fpsr = fpctx->fpsr;
|
||||
guest_ctx->fpcr = fpctx->fpcr;
|
||||
guest_ctx->pstate = static_cast<u32>(host_ctx.pstate);
|
||||
guest_ctx->pc = host_ctx.pc;
|
||||
guest_ctx->sp = host_ctx.sp;
|
||||
|
||||
// Restore stack pointer.
|
||||
host_ctx.sp = guest_ctx->host_ctx.host_sp;
|
||||
|
||||
// Restore host callee-saved registers.
|
||||
std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(),
|
||||
sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(),
|
||||
sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
|
||||
// Return from the call on exit by setting pc to x30.
|
||||
host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11];
|
||||
|
||||
// Clear esr_el1 and return it.
|
||||
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
|
||||
}
|
||||
|
||||
bool ARM_NCE::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||
|
||||
// Try to handle an invalid access.
|
||||
// TODO: handle accesses which split a page?
|
||||
const Common::ProcessAddress addr =
|
||||
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
||||
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
||||
// We handled the access successfully and are returning to guest code.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can't handle the access, so determine why we crashed.
|
||||
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
|
||||
|
||||
// For data aborts, skip the instruction and return to guest code.
|
||||
// This will allow games to continue in many scenarios where they would otherwise crash.
|
||||
if (!is_prefetch_abort) {
|
||||
host_ctx.pc += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is a prefetch abort.
|
||||
guest_ctx->esr_el1.fetch_or(static_cast<u64>(HaltReason::PrefetchAbort));
|
||||
|
||||
// Forcibly mark the context as locked. We are still running.
|
||||
// We may race with SignalInterrupt here:
|
||||
// - If we lose the race, then SignalInterrupt will send us a signal we are masking,
|
||||
// and it will do nothing when it is unmasked, as we have already left guest code.
|
||||
// - If we win the race, then SignalInterrupt will wait for us to unlock first.
|
||||
auto& thread_params = guest_ctx->parent->running_thread->GetNativeExecutionParameters();
|
||||
thread_params.lock.store(SpinLockLocked);
|
||||
|
||||
// Return to host.
|
||||
SaveGuestContext(guest_ctx, raw_context);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ARM_NCE::HandleHostFault(int sig, void* raw_info, void* raw_context) {
|
||||
return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
|
||||
}
|
||||
|
||||
HaltReason ARM_NCE::RunJit() {
|
||||
// Get the thread parameters.
|
||||
// TODO: pass the current thread down from ::Run
|
||||
auto* thread = Kernel::GetCurrentThreadPointer(system.Kernel());
|
||||
auto* thread_params = &thread->GetNativeExecutionParameters();
|
||||
|
||||
{
|
||||
// Lock our core context.
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
// We should not be running.
|
||||
ASSERT(running_thread == nullptr);
|
||||
|
||||
// Check if we need to run. If we have already been halted, we are done.
|
||||
u64 halt = guest_ctx.esr_el1.exchange(0);
|
||||
if (halt != 0) {
|
||||
return static_cast<HaltReason>(halt);
|
||||
}
|
||||
|
||||
// Mark that we are running.
|
||||
running_thread = thread;
|
||||
|
||||
// Acquire the lock on the thread parameters.
|
||||
// This allows us to force synchronization with SignalInterrupt.
|
||||
LockThreadParameters(thread_params);
|
||||
}
|
||||
|
||||
// Assign current members.
|
||||
guest_ctx.parent = this;
|
||||
thread_params->native_context = &guest_ctx;
|
||||
thread_params->tpidr_el0 = guest_ctx.tpidr_el0;
|
||||
thread_params->tpidrro_el0 = guest_ctx.tpidrro_el0;
|
||||
thread_params->is_running = true;
|
||||
|
||||
HaltReason halt{};
|
||||
|
||||
// TODO: finding and creating the post handler needs to be locked
|
||||
// to deal with dynamic loading of NROs.
|
||||
const auto& post_handlers = system.ApplicationProcess()->GetPostHandlers();
|
||||
if (auto it = post_handlers.find(guest_ctx.pc); it != post_handlers.end()) {
|
||||
halt = ReturnToRunCodeByTrampoline(thread_params, &guest_ctx, it->second);
|
||||
} else {
|
||||
halt = ReturnToRunCodeByExceptionLevelChange(thread_id, thread_params);
|
||||
}
|
||||
|
||||
// Unload members.
|
||||
// The thread does not change, so we can persist the old reference.
|
||||
guest_ctx.tpidr_el0 = thread_params->tpidr_el0;
|
||||
thread_params->native_context = nullptr;
|
||||
thread_params->is_running = false;
|
||||
|
||||
// Unlock the thread parameters.
|
||||
UnlockThreadParameters(thread_params);
|
||||
|
||||
{
|
||||
// Lock the core context.
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
// On exit, we no longer have an active thread.
|
||||
running_thread = nullptr;
|
||||
}
|
||||
|
||||
// Return the halt reason.
|
||||
return halt;
|
||||
}
|
||||
|
||||
HaltReason ARM_NCE::StepJit() {
|
||||
return HaltReason::StepThread;
|
||||
}
|
||||
|
||||
u32 ARM_NCE::GetSvcNumber() const {
|
||||
return guest_ctx.svc_swi;
|
||||
}
|
||||
|
||||
ARM_NCE::ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_)
|
||||
: ARM_Interface{system_, uses_wall_clock_}, core_index{core_index_} {
|
||||
guest_ctx.system = &system_;
|
||||
}
|
||||
|
||||
ARM_NCE::~ARM_NCE() = default;
|
||||
|
||||
void ARM_NCE::Initialize() {
|
||||
thread_id = gettid();
|
||||
|
||||
// Setup our signals
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [] {
|
||||
using HandlerType = decltype(sigaction::sa_sigaction);
|
||||
|
||||
sigset_t signal_mask;
|
||||
sigemptyset(&signal_mask);
|
||||
sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
|
||||
sigaddset(&signal_mask, BreakFromRunCodeSignal);
|
||||
sigaddset(&signal_mask, GuestFaultSignal);
|
||||
|
||||
struct sigaction return_to_run_code_action {};
|
||||
return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
return_to_run_code_action.sa_sigaction = reinterpret_cast<HandlerType>(
|
||||
&ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler);
|
||||
return_to_run_code_action.sa_mask = signal_mask;
|
||||
Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action,
|
||||
nullptr);
|
||||
|
||||
struct sigaction break_from_run_code_action {};
|
||||
break_from_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
break_from_run_code_action.sa_sigaction =
|
||||
reinterpret_cast<HandlerType>(&ARM_NCE::BreakFromRunCodeSignalHandler);
|
||||
break_from_run_code_action.sa_mask = signal_mask;
|
||||
Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
|
||||
|
||||
struct sigaction fault_action {};
|
||||
fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
||||
fault_action.sa_sigaction =
|
||||
reinterpret_cast<HandlerType>(&ARM_NCE::GuestFaultSignalHandler);
|
||||
fault_action.sa_mask = signal_mask;
|
||||
Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action);
|
||||
|
||||
// Simplify call for g_orig_action.
|
||||
// These fields occupy the same space in memory, so this should be a no-op in practice.
|
||||
if (!(g_orig_action.sa_flags & SA_SIGINFO)) {
|
||||
g_orig_action.sa_sigaction =
|
||||
reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ARM_NCE::SetPC(u64 pc) {
|
||||
guest_ctx.pc = pc;
|
||||
}
|
||||
|
||||
u64 ARM_NCE::GetPC() const {
|
||||
return guest_ctx.pc;
|
||||
}
|
||||
|
||||
u64 ARM_NCE::GetSP() const {
|
||||
return guest_ctx.sp;
|
||||
}
|
||||
|
||||
u64 ARM_NCE::GetReg(int index) const {
|
||||
return guest_ctx.cpu_registers[index];
|
||||
}
|
||||
|
||||
void ARM_NCE::SetReg(int index, u64 value) {
|
||||
guest_ctx.cpu_registers[index] = value;
|
||||
}
|
||||
|
||||
u128 ARM_NCE::GetVectorReg(int index) const {
|
||||
return guest_ctx.vector_registers[index];
|
||||
}
|
||||
|
||||
void ARM_NCE::SetVectorReg(int index, u128 value) {
|
||||
guest_ctx.vector_registers[index] = value;
|
||||
}
|
||||
|
||||
u32 ARM_NCE::GetPSTATE() const {
|
||||
return guest_ctx.pstate;
|
||||
}
|
||||
|
||||
void ARM_NCE::SetPSTATE(u32 pstate) {
|
||||
guest_ctx.pstate = pstate;
|
||||
}
|
||||
|
||||
u64 ARM_NCE::GetTlsAddress() const {
|
||||
return guest_ctx.tpidrro_el0;
|
||||
}
|
||||
|
||||
void ARM_NCE::SetTlsAddress(u64 address) {
|
||||
guest_ctx.tpidrro_el0 = address;
|
||||
}
|
||||
|
||||
u64 ARM_NCE::GetTPIDR_EL0() const {
|
||||
return guest_ctx.tpidr_el0;
|
||||
}
|
||||
|
||||
void ARM_NCE::SetTPIDR_EL0(u64 value) {
|
||||
guest_ctx.tpidr_el0 = value;
|
||||
}
|
||||
|
||||
void ARM_NCE::SaveContext(ThreadContext64& ctx) const {
|
||||
ctx.cpu_registers = guest_ctx.cpu_registers;
|
||||
ctx.sp = guest_ctx.sp;
|
||||
ctx.pc = guest_ctx.pc;
|
||||
ctx.pstate = guest_ctx.pstate;
|
||||
ctx.vector_registers = guest_ctx.vector_registers;
|
||||
ctx.fpcr = guest_ctx.fpcr;
|
||||
ctx.fpsr = guest_ctx.fpsr;
|
||||
ctx.tpidr = guest_ctx.tpidr_el0;
|
||||
}
|
||||
|
||||
void ARM_NCE::LoadContext(const ThreadContext64& ctx) {
|
||||
guest_ctx.cpu_registers = ctx.cpu_registers;
|
||||
guest_ctx.sp = ctx.sp;
|
||||
guest_ctx.pc = ctx.pc;
|
||||
guest_ctx.pstate = ctx.pstate;
|
||||
guest_ctx.vector_registers = ctx.vector_registers;
|
||||
guest_ctx.fpcr = ctx.fpcr;
|
||||
guest_ctx.fpsr = ctx.fpsr;
|
||||
guest_ctx.tpidr_el0 = ctx.tpidr;
|
||||
}
|
||||
|
||||
void ARM_NCE::SignalInterrupt() {
|
||||
// Lock core context.
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
// Add break loop condition.
|
||||
guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop));
|
||||
|
||||
// If there is no thread running, we are done.
|
||||
if (running_thread == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock the thread context.
|
||||
auto* params = &running_thread->GetNativeExecutionParameters();
|
||||
LockThreadParameters(params);
|
||||
|
||||
if (params->is_running) {
|
||||
// We should signal to the running thread.
|
||||
// The running thread will unlock the thread context.
|
||||
syscall(SYS_tkill, thread_id, BreakFromRunCodeSignal);
|
||||
} else {
|
||||
// If the thread is no longer running, we have nothing to do.
|
||||
UnlockThreadParameters(params);
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_NCE::ClearInterrupt() {
|
||||
guest_ctx.esr_el1 = {};
|
||||
}
|
||||
|
||||
void ARM_NCE::ClearInstructionCache() {
|
||||
// TODO: This is not possible to implement correctly on Linux because
|
||||
// we do not have any access to ic iallu.
|
||||
|
||||
// Require accesses to complete.
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
void ARM_NCE::InvalidateCacheRange(u64 addr, std::size_t size) {
|
||||
this->ClearInstructionCache();
|
||||
}
|
||||
|
||||
void ARM_NCE::ClearExclusiveState() {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
void ARM_NCE::PageTableChanged(Common::PageTable& page_table,
|
||||
std::size_t new_address_space_size_in_bits) {
|
||||
// No-op. Page table is never used.
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
108
src/core/arm/nce/arm_nce.h
Normal file
108
src/core/arm/nce/arm_nce.h
Normal file
@@ -0,0 +1,108 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/nce/guest_context.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class System;
|
||||
|
||||
class ARM_NCE final : public ARM_Interface {
|
||||
public:
|
||||
ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_);
|
||||
|
||||
~ARM_NCE() override;
|
||||
|
||||
void Initialize() override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetSP() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
void SetReg(int index, u64 value) override;
|
||||
u128 GetVectorReg(int index) const override;
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
u64 GetTlsAddress() const override;
|
||||
void SetTlsAddress(u64 address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
|
||||
Architecture GetArchitecture() const override {
|
||||
return Architecture::Aarch64;
|
||||
}
|
||||
|
||||
void SaveContext(ThreadContext32& ctx) const override {}
|
||||
void SaveContext(ThreadContext64& ctx) const override;
|
||||
void LoadContext(const ThreadContext32& ctx) override {}
|
||||
void LoadContext(const ThreadContext64& ctx) override;
|
||||
|
||||
void SignalInterrupt() override;
|
||||
void ClearInterrupt() override;
|
||||
void ClearExclusiveState() override;
|
||||
void ClearInstructionCache() override;
|
||||
void InvalidateCacheRange(u64 addr, std::size_t size) override;
|
||||
void PageTableChanged(Common::PageTable& new_page_table,
|
||||
std::size_t new_address_space_size_in_bits) override;
|
||||
|
||||
protected:
|
||||
HaltReason RunJit() override;
|
||||
HaltReason StepJit() override;
|
||||
|
||||
u32 GetSvcNumber() const override;
|
||||
|
||||
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RewindBreakpointInstruction() override {}
|
||||
|
||||
private:
|
||||
// Assembly definitions.
|
||||
static HaltReason ReturnToRunCodeByTrampoline(void* tpidr, GuestContext* ctx,
|
||||
u64 trampoline_addr);
|
||||
static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr);
|
||||
|
||||
static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
|
||||
void* raw_context);
|
||||
static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
|
||||
static void GuestFaultSignalHandler(int sig, void* info, void* raw_context);
|
||||
|
||||
static void LockThreadParameters(void* tpidr);
|
||||
static void UnlockThreadParameters(void* tpidr);
|
||||
|
||||
private:
|
||||
// C++ implementation functions for assembly definitions.
|
||||
static void* RestoreGuestContext(void* raw_context);
|
||||
static void SaveGuestContext(GuestContext* ctx, void* raw_context);
|
||||
static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context);
|
||||
static void HandleHostFault(int sig, void* info, void* raw_context);
|
||||
|
||||
public:
|
||||
// Members set on initialization.
|
||||
std::size_t core_index{};
|
||||
pid_t thread_id{-1};
|
||||
|
||||
// Core context.
|
||||
GuestContext guest_ctx;
|
||||
|
||||
// Thread and invalidation info.
|
||||
std::mutex lock;
|
||||
Kernel::KThread* running_thread{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
222
src/core/arm/nce/arm_nce.s
Normal file
222
src/core/arm/nce/arm_nce.s
Normal file
@@ -0,0 +1,222 @@
|
||||
/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "core/arm/nce/arm_nce_asm_definitions.h"
|
||||
|
||||
#define LOAD_IMMEDIATE_32(reg, val) \
|
||||
mov reg, #(((val) >> 0x00) & 0xFFFF); \
|
||||
movk reg, #(((val) >> 0x10) & 0xFFFF), lsl #16
|
||||
|
||||
|
||||
/* static HaltReason Core::ARM_NCE::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */
|
||||
.section .text._ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, "ax", %progbits
|
||||
.global _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm
|
||||
.type _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, %function
|
||||
_ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
|
||||
/* Back up host sp to x3. */
|
||||
/* Back up host tpidr_el0 to x4. */
|
||||
mov x3, sp
|
||||
mrs x4, tpidr_el0
|
||||
|
||||
/* Load guest sp. x5 is used as a scratch register. */
|
||||
ldr x5, [x1, #(GuestContextSp)]
|
||||
mov sp, x5
|
||||
|
||||
/* Offset GuestContext pointer to the host member. */
|
||||
add x5, x1, #(GuestContextHostContext)
|
||||
|
||||
/* Save original host sp and tpidr_el0 (x3, x4) to host context. */
|
||||
stp x3, x4, [x5, #(HostContextSpTpidrEl0)]
|
||||
|
||||
/* Save all callee-saved host GPRs. */
|
||||
stp x19, x20, [x5, #(HostContextRegs+0x0)]
|
||||
stp x21, x22, [x5, #(HostContextRegs+0x10)]
|
||||
stp x23, x24, [x5, #(HostContextRegs+0x20)]
|
||||
stp x25, x26, [x5, #(HostContextRegs+0x30)]
|
||||
stp x27, x28, [x5, #(HostContextRegs+0x40)]
|
||||
stp x29, x30, [x5, #(HostContextRegs+0x50)]
|
||||
|
||||
/* Save all callee-saved host FPRs. */
|
||||
stp q8, q9, [x5, #(HostContextVregs+0x0)]
|
||||
stp q10, q11, [x5, #(HostContextVregs+0x20)]
|
||||
stp q12, q13, [x5, #(HostContextVregs+0x40)]
|
||||
stp q14, q15, [x5, #(HostContextVregs+0x60)]
|
||||
|
||||
/* Load guest tpidr_el0 from argument. */
|
||||
msr tpidr_el0, x0
|
||||
|
||||
/* Tail call the trampoline to restore guest state. */
|
||||
br x2
|
||||
|
||||
|
||||
/* static HaltReason Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */
|
||||
.section .text._ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, "ax", %progbits
|
||||
.global _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv
|
||||
.type _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, %function
|
||||
_ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv:
|
||||
/* This jumps to the signal handler, which will restore the entire context. */
|
||||
/* On entry, x0 = thread id, which is already in the right place. */
|
||||
|
||||
/* Move tpidr to x9 so it is not trampled. */
|
||||
mov x9, x1
|
||||
|
||||
/* Set up arguments. */
|
||||
mov x8, #(__NR_tkill)
|
||||
mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal)
|
||||
|
||||
/* Tail call the signal handler. */
|
||||
svc #0
|
||||
|
||||
/* Block execution from flowing here. */
|
||||
brk #1000
|
||||
|
||||
|
||||
/* static void Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */
|
||||
.section .text._ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, "ax", %progbits
|
||||
.global _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
|
||||
.type _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, %function
|
||||
_ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
|
||||
stp x29, x30, [sp, #-0x10]!
|
||||
mov x29, sp
|
||||
|
||||
/* Call the context restorer with the raw context. */
|
||||
mov x0, x2
|
||||
bl _ZN4Core7ARM_NCE19RestoreGuestContextEPv
|
||||
|
||||
/* Save the old value of tpidr_el0. */
|
||||
mrs x8, tpidr_el0
|
||||
ldr x9, [x0, #(TpidrEl0NativeContext)]
|
||||
str x8, [x9, #(GuestContextHostContext + HostContextTpidrEl0)]
|
||||
|
||||
/* Set our new tpidr_el0. */
|
||||
msr tpidr_el0, x0
|
||||
|
||||
/* Unlock the context. */
|
||||
bl _ZN4Core7ARM_NCE22UnlockThreadParametersEPv
|
||||
|
||||
/* Returning from here will enter the guest. */
|
||||
ldp x29, x30, [sp], #0x10
|
||||
ret
|
||||
|
||||
|
||||
/* static void Core::ARM_NCE::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */
|
||||
.section .text._ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, "ax", %progbits
|
||||
.global _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_
|
||||
.type _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, %function
|
||||
_ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_:
|
||||
/* Check to see if we have the correct TLS magic. */
|
||||
mrs x8, tpidr_el0
|
||||
ldr w9, [x8, #(TpidrEl0TlsMagic)]
|
||||
|
||||
LOAD_IMMEDIATE_32(w10, TlsMagic)
|
||||
|
||||
cmp w9, w10
|
||||
b.ne 1f
|
||||
|
||||
/* Correct TLS magic, so this is a guest interrupt. */
|
||||
/* Restore host tpidr_el0. */
|
||||
ldr x0, [x8, #(TpidrEl0NativeContext)]
|
||||
ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
|
||||
msr tpidr_el0, x3
|
||||
|
||||
/* Tail call the restorer. */
|
||||
mov x1, x2
|
||||
b _ZN4Core7ARM_NCE16SaveGuestContextEPNS_12GuestContextEPv
|
||||
|
||||
/* Returning from here will enter host code. */
|
||||
|
||||
1:
|
||||
/* Incorrect TLS magic, so this is a spurious signal. */
|
||||
ret
|
||||
|
||||
|
||||
/* static void Core::ARM_NCE::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */
|
||||
.section .text._ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits
|
||||
.global _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_
|
||||
.type _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, %function
|
||||
_ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_:
|
||||
/* Check to see if we have the correct TLS magic. */
|
||||
mrs x8, tpidr_el0
|
||||
ldr w9, [x8, #(TpidrEl0TlsMagic)]
|
||||
|
||||
LOAD_IMMEDIATE_32(w10, TlsMagic)
|
||||
|
||||
cmp w9, w10
|
||||
b.eq 1f
|
||||
|
||||
/* Incorrect TLS magic, so this is a host fault. */
|
||||
/* Tail call the handler. */
|
||||
b _ZN4Core7ARM_NCE15HandleHostFaultEiPvS1_
|
||||
|
||||
1:
|
||||
/* Correct TLS magic, so this is a guest fault. */
|
||||
stp x29, x30, [sp, #-0x20]!
|
||||
str x19, [sp, #0x10]
|
||||
mov x29, sp
|
||||
|
||||
/* Save the old tpidr_el0. */
|
||||
mov x19, x8
|
||||
|
||||
/* Restore host tpidr_el0. */
|
||||
ldr x0, [x8, #(TpidrEl0NativeContext)]
|
||||
ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
|
||||
msr tpidr_el0, x3
|
||||
|
||||
/* Call the handler. */
|
||||
bl _ZN4Core7ARM_NCE16HandleGuestFaultEPNS_12GuestContextEPvS3_
|
||||
|
||||
/* If the handler returned false, we want to preserve the host tpidr_el0. */
|
||||
cbz x0, 2f
|
||||
|
||||
/* Otherwise, restore guest tpidr_el0. */
|
||||
msr tpidr_el0, x19
|
||||
|
||||
2:
|
||||
ldr x19, [sp, #0x10]
|
||||
ldp x29, x30, [sp], #0x20
|
||||
ret
|
||||
|
||||
|
||||
/* static void Core::ARM_NCE::LockThreadParameters(void* tpidr) */
|
||||
.section .text._ZN4Core7ARM_NCE20LockThreadParametersEPv, "ax", %progbits
|
||||
.global _ZN4Core7ARM_NCE20LockThreadParametersEPv
|
||||
.type _ZN4Core7ARM_NCE20LockThreadParametersEPv, %function
|
||||
_ZN4Core7ARM_NCE20LockThreadParametersEPv:
|
||||
/* Offset to lock member. */
|
||||
add x0, x0, #(TpidrEl0Lock)
|
||||
|
||||
1:
|
||||
/* Clear the monitor. */
|
||||
clrex
|
||||
|
||||
2:
|
||||
/* Load-linked with acquire ordering. */
|
||||
ldaxr w1, [x0]
|
||||
|
||||
/* If the value was SpinLockLocked, clear monitor and retry. */
|
||||
cbz w1, 1b
|
||||
|
||||
/* Store-conditional SpinLockLocked with relaxed ordering. */
|
||||
stxr w1, wzr, [x0]
|
||||
|
||||
/* If we failed to store, retry. */
|
||||
cbnz w1, 2b
|
||||
|
||||
ret
|
||||
|
||||
|
||||
/* static void Core::ARM_NCE::UnlockThreadParameters(void* tpidr) */
|
||||
.section .text._ZN4Core7ARM_NCE22UnlockThreadParametersEPv, "ax", %progbits
|
||||
.global _ZN4Core7ARM_NCE22UnlockThreadParametersEPv
|
||||
.type _ZN4Core7ARM_NCE22UnlockThreadParametersEPv, %function
|
||||
_ZN4Core7ARM_NCE22UnlockThreadParametersEPv:
|
||||
/* Offset to lock member. */
|
||||
add x0, x0, #(TpidrEl0Lock)
|
||||
|
||||
/* Load SpinLockUnlocked. */
|
||||
mov w1, #(SpinLockUnlocked)
|
||||
|
||||
/* Store value with release ordering. */
|
||||
stlr w1, [x0]
|
||||
|
||||
ret
|
||||
29
src/core/arm/nce/arm_nce_asm_definitions.h
Normal file
29
src/core/arm/nce/arm_nce_asm_definitions.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define __ASSEMBLY__
|
||||
|
||||
#include <asm-generic/signal.h>
|
||||
#include <asm-generic/unistd.h>
|
||||
|
||||
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
|
||||
#define BreakFromRunCodeSignal SIGURG
|
||||
#define GuestFaultSignal SIGSEGV
|
||||
|
||||
#define GuestContextSp 0xF8
|
||||
#define GuestContextHostContext 0x320
|
||||
|
||||
#define HostContextSpTpidrEl0 0xE0
|
||||
#define HostContextTpidrEl0 0xE8
|
||||
#define HostContextRegs 0x0
|
||||
#define HostContextVregs 0x60
|
||||
|
||||
#define TpidrEl0NativeContext 0x10
|
||||
#define TpidrEl0Lock 0x18
|
||||
#define TpidrEl0TlsMagic 0x20
|
||||
#define TlsMagic 0x555a5559
|
||||
|
||||
#define SpinLockLocked 0
|
||||
#define SpinLockUnlocked 1
|
||||
50
src/core/arm/nce/guest_context.h
Normal file
50
src/core/arm/nce/guest_context.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/nce/arm_nce_asm_definitions.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_NCE;
|
||||
class System;
|
||||
|
||||
struct HostContext {
|
||||
alignas(16) std::array<u64, 12> host_saved_regs{};
|
||||
alignas(16) std::array<u128, 8> host_saved_vregs{};
|
||||
u64 host_sp{};
|
||||
void* host_tpidr_el0{};
|
||||
};
|
||||
|
||||
struct GuestContext {
|
||||
std::array<u64, 31> cpu_registers{};
|
||||
u64 sp{};
|
||||
u64 pc{};
|
||||
u32 fpcr{};
|
||||
u32 fpsr{};
|
||||
std::array<u128, 32> vector_registers{};
|
||||
u32 pstate{};
|
||||
alignas(16) HostContext host_ctx{};
|
||||
u64 tpidrro_el0{};
|
||||
u64 tpidr_el0{};
|
||||
std::atomic<u64> esr_el1{};
|
||||
u32 nzcv{};
|
||||
u32 svc_swi{};
|
||||
System* system{};
|
||||
ARM_NCE* parent{};
|
||||
};
|
||||
|
||||
// Verify assembly offsets.
|
||||
static_assert(offsetof(GuestContext, sp) == GuestContextSp);
|
||||
static_assert(offsetof(GuestContext, host_ctx) == GuestContextHostContext);
|
||||
static_assert(offsetof(HostContext, host_sp) == HostContextSpTpidrEl0);
|
||||
static_assert(offsetof(HostContext, host_tpidr_el0) - 8 == HostContextSpTpidrEl0);
|
||||
static_assert(offsetof(HostContext, host_tpidr_el0) == HostContextTpidrEl0);
|
||||
static_assert(offsetof(HostContext, host_saved_regs) == HostContextRegs);
|
||||
static_assert(offsetof(HostContext, host_saved_vregs) == HostContextVregs);
|
||||
|
||||
} // namespace Core
|
||||
147
src/core/arm/nce/instructions.h
Normal file
147
src/core/arm/nce/instructions.h
Normal file
@@ -0,0 +1,147 @@
|
||||
// SPDX-FileCopyrightText: Copyright © 2020 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::NCE {
|
||||
|
||||
enum SystemRegister : u32 {
|
||||
TpidrEl0 = 0x5E82,
|
||||
TpidrroEl0 = 0x5E83,
|
||||
CntfrqEl0 = 0x5F00,
|
||||
CntpctEl0 = 0x5F01,
|
||||
};
|
||||
|
||||
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/SVC--Supervisor-Call-
|
||||
union SVC {
|
||||
constexpr explicit SVC(u32 raw_) : raw{raw_} {}
|
||||
|
||||
constexpr bool Verify() {
|
||||
return (this->GetSig0() == 0x1 && this->GetSig1() == 0x6A0);
|
||||
}
|
||||
|
||||
constexpr u32 GetSig0() {
|
||||
return decltype(sig0)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
constexpr u32 GetValue() {
|
||||
return decltype(value)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
constexpr u32 GetSig1() {
|
||||
return decltype(sig1)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
u32 raw;
|
||||
|
||||
private:
|
||||
BitField<0, 5, u32> sig0; // 0x1
|
||||
BitField<5, 16, u32> value; // 16-bit immediate
|
||||
BitField<21, 11, u32> sig1; // 0x6A0
|
||||
};
|
||||
static_assert(sizeof(SVC) == sizeof(u32));
|
||||
static_assert(SVC(0xD40000C1).Verify());
|
||||
static_assert(SVC(0xD40000C1).GetValue() == 0x6);
|
||||
|
||||
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MRS--Move-System-Register-
|
||||
union MRS {
|
||||
constexpr explicit MRS(u32 raw_) : raw{raw_} {}
|
||||
|
||||
constexpr bool Verify() {
|
||||
return (this->GetSig() == 0xD53);
|
||||
}
|
||||
|
||||
constexpr u32 GetRt() {
|
||||
return decltype(rt)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
constexpr u32 GetSystemReg() {
|
||||
return decltype(system_reg)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
constexpr u32 GetSig() {
|
||||
return decltype(sig)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
u32 raw;
|
||||
|
||||
private:
|
||||
BitField<0, 5, u32> rt; // destination register
|
||||
BitField<5, 15, u32> system_reg; // source system register
|
||||
BitField<20, 12, u32> sig; // 0xD53
|
||||
};
|
||||
static_assert(sizeof(MRS) == sizeof(u32));
|
||||
static_assert(MRS(0xD53BE020).Verify());
|
||||
static_assert(MRS(0xD53BE020).GetSystemReg() == CntpctEl0);
|
||||
static_assert(MRS(0xD53BE020).GetRt() == 0x0);
|
||||
|
||||
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MSR--register---Move-general-purpose-register-to-System-Register-
|
||||
union MSR {
|
||||
constexpr explicit MSR(u32 raw_) : raw{raw_} {}
|
||||
|
||||
constexpr bool Verify() {
|
||||
return this->GetSig() == 0xD51;
|
||||
}
|
||||
|
||||
constexpr u32 GetRt() {
|
||||
return decltype(rt)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
constexpr u32 GetSystemReg() {
|
||||
return decltype(system_reg)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
constexpr u32 GetSig() {
|
||||
return decltype(sig)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
u32 raw;
|
||||
|
||||
private:
|
||||
BitField<0, 5, u32> rt; // source register
|
||||
BitField<5, 15, u32> system_reg; // destination system register
|
||||
BitField<20, 12, u32> sig; // 0xD51
|
||||
};
|
||||
static_assert(sizeof(MSR) == sizeof(u32));
|
||||
static_assert(MSR(0xD51BD040).Verify());
|
||||
static_assert(MSR(0xD51BD040).GetSystemReg() == TpidrEl0);
|
||||
static_assert(MSR(0xD51BD040).GetRt() == 0x0);
|
||||
|
||||
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXR--Load-Exclusive-Register-
|
||||
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXP--Load-Exclusive-Pair-of-Registers-
|
||||
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXR--Store-Exclusive-Register-
|
||||
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXP--Store-Exclusive-Pair-of-registers-
|
||||
union Exclusive {
|
||||
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
||||
|
||||
constexpr bool Verify() {
|
||||
return this->GetSig() == 0x10;
|
||||
}
|
||||
|
||||
constexpr u32 GetSig() {
|
||||
return decltype(sig)::ExtractValue(raw);
|
||||
}
|
||||
|
||||
constexpr u32 AsOrdered() {
|
||||
return raw | decltype(o0)::FormatValue(1);
|
||||
}
|
||||
|
||||
u32 raw;
|
||||
|
||||
private:
|
||||
BitField<0, 5, u32> rt; // memory operand
|
||||
BitField<5, 5, u32> rn; // register operand 1
|
||||
BitField<10, 5, u32> rt2; // register operand 2
|
||||
BitField<15, 1, u32> o0; // ordered
|
||||
BitField<16, 5, u32> rs; // status register
|
||||
BitField<21, 2, u32> l; // operation type
|
||||
BitField<23, 7, u32> sig; // 0x10
|
||||
BitField<30, 2, u32> size; // size
|
||||
};
|
||||
static_assert(Exclusive(0xC85FFC00).Verify());
|
||||
static_assert(Exclusive(0xC85FFC00).AsOrdered() == 0xC85FFC00);
|
||||
static_assert(Exclusive(0xC85F7C00).AsOrdered() == 0xC85FFC00);
|
||||
static_assert(Exclusive(0xC8200440).AsOrdered() == 0xC8208440);
|
||||
|
||||
} // namespace Core::NCE
|
||||
474
src/core/arm/nce/patch.cpp
Normal file
474
src/core/arm/nce/patch.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/arm64/native_clock.h"
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/literals.h"
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
#include "core/arm/nce/guest_context.h"
|
||||
#include "core/arm/nce/instructions.h"
|
||||
#include "core/arm/nce/patch.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
|
||||
namespace Core::NCE {
|
||||
|
||||
using namespace Common::Literals;
|
||||
using namespace oaknut::util;
|
||||
|
||||
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
|
||||
|
||||
constexpr size_t MaxRelativeBranch = 128_MiB;
|
||||
constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
|
||||
|
||||
Patcher::Patcher() : c(m_patch_instructions) {}
|
||||
|
||||
Patcher::~Patcher() = default;
|
||||
|
||||
void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
||||
const Kernel::CodeSet::Segment& code) {
|
||||
|
||||
// Write save context helper function.
|
||||
c.l(m_save_context);
|
||||
WriteSaveContext();
|
||||
|
||||
// Write load context helper function.
|
||||
c.l(m_load_context);
|
||||
WriteLoadContext();
|
||||
|
||||
// Retrieve text segment data.
|
||||
const auto text = std::span{program_image}.subspan(code.offset, code.size);
|
||||
const auto text_words =
|
||||
std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)};
|
||||
|
||||
// Loop through instructions, patching as needed.
|
||||
for (u32 i = ModuleCodeIndex; i < static_cast<u32>(text_words.size()); i++) {
|
||||
const u32 inst = text_words[i];
|
||||
|
||||
const auto AddRelocations = [&] {
|
||||
const uintptr_t this_offset = i * sizeof(u32);
|
||||
const uintptr_t next_offset = this_offset + sizeof(u32);
|
||||
|
||||
// Relocate from here to patch.
|
||||
this->BranchToPatch(this_offset);
|
||||
|
||||
// Relocate from patch to next instruction.
|
||||
return next_offset;
|
||||
};
|
||||
|
||||
// SVC
|
||||
if (auto svc = SVC{inst}; svc.Verify()) {
|
||||
WriteSvcTrampoline(AddRelocations(), svc.GetValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
// MRS Xn, TPIDR_EL0
|
||||
// MRS Xn, TPIDRRO_EL0
|
||||
if (auto mrs = MRS{inst};
|
||||
mrs.Verify() && (mrs.GetSystemReg() == TpidrroEl0 || mrs.GetSystemReg() == TpidrEl0)) {
|
||||
const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0
|
||||
: oaknut::SystemReg::TPIDR_EL0;
|
||||
const auto dest_reg = oaknut::XReg{static_cast<int>(mrs.GetRt())};
|
||||
WriteMrsHandler(AddRelocations(), dest_reg, src_reg);
|
||||
continue;
|
||||
}
|
||||
|
||||
// MRS Xn, CNTPCT_EL0
|
||||
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntpctEl0) {
|
||||
WriteCntpctHandler(AddRelocations(), oaknut::XReg{static_cast<int>(mrs.GetRt())});
|
||||
continue;
|
||||
}
|
||||
|
||||
// MRS Xn, CNTFRQ_EL0
|
||||
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// MSR TPIDR_EL0, Xn
|
||||
if (auto msr = MSR{inst}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) {
|
||||
WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
|
||||
m_exclusives.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine patching mode for the final relocation step
|
||||
const size_t image_size = program_image.size();
|
||||
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
|
||||
}
|
||||
|
||||
void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
||||
const Kernel::CodeSet::Segment& code,
|
||||
Kernel::PhysicalMemory& program_image,
|
||||
EntryTrampolines* out_trampolines) {
|
||||
const size_t patch_size = GetSectionSize();
|
||||
const size_t image_size = program_image.size();
|
||||
|
||||
// Retrieve text segment data.
|
||||
const auto text = std::span{program_image}.subspan(code.offset, code.size);
|
||||
const auto text_words =
|
||||
std::span<u32>{reinterpret_cast<u32*>(text.data()), text.size() / sizeof(u32)};
|
||||
|
||||
const auto ApplyBranchToPatchRelocation = [&](u32* target, const Relocation& rel) {
|
||||
oaknut::CodeGenerator rc{target};
|
||||
if (mode == PatchMode::PreText) {
|
||||
rc.B(rel.patch_offset - patch_size - rel.module_offset);
|
||||
} else {
|
||||
rc.B(image_size - rel.module_offset + rel.patch_offset);
|
||||
}
|
||||
};
|
||||
|
||||
const auto ApplyBranchToModuleRelocation = [&](u32* target, const Relocation& rel) {
|
||||
oaknut::CodeGenerator rc{target};
|
||||
if (mode == PatchMode::PreText) {
|
||||
rc.B(patch_size - rel.patch_offset + rel.module_offset);
|
||||
} else {
|
||||
rc.B(rel.module_offset - image_size - rel.patch_offset);
|
||||
}
|
||||
};
|
||||
|
||||
const auto RebasePatch = [&](ptrdiff_t patch_offset) {
|
||||
if (mode == PatchMode::PreText) {
|
||||
return GetInteger(load_base) + patch_offset;
|
||||
} else {
|
||||
return GetInteger(load_base) + image_size + patch_offset;
|
||||
}
|
||||
};
|
||||
|
||||
const auto RebasePc = [&](uintptr_t module_offset) {
|
||||
if (mode == PatchMode::PreText) {
|
||||
return GetInteger(load_base) + patch_size + module_offset;
|
||||
} else {
|
||||
return GetInteger(load_base) + module_offset;
|
||||
}
|
||||
};
|
||||
|
||||
// We are now ready to relocate!
|
||||
for (const Relocation& rel : m_branch_to_patch_relocations) {
|
||||
ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
|
||||
}
|
||||
for (const Relocation& rel : m_branch_to_module_relocations) {
|
||||
ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
|
||||
rel);
|
||||
}
|
||||
|
||||
// Rewrite PC constants and record post trampolines
|
||||
for (const Relocation& rel : m_write_module_pc_relocations) {
|
||||
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
|
||||
rc.dx(RebasePc(rel.module_offset));
|
||||
}
|
||||
for (const Trampoline& rel : m_trampolines) {
|
||||
out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
|
||||
}
|
||||
|
||||
// Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
|
||||
// Convert to ordered to preserve this assumption.
|
||||
for (const ModuleTextAddress i : m_exclusives) {
|
||||
auto exclusive = Exclusive{text_words[i]};
|
||||
text_words[i] = exclusive.AsOrdered();
|
||||
}
|
||||
|
||||
// Copy to program image
|
||||
if (this->mode == PatchMode::PreText) {
|
||||
std::memcpy(program_image.data(), m_patch_instructions.data(),
|
||||
m_patch_instructions.size() * sizeof(u32));
|
||||
} else {
|
||||
program_image.resize(image_size + patch_size);
|
||||
std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
|
||||
m_patch_instructions.size() * sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
size_t Patcher::GetSectionSize() const noexcept {
|
||||
return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
|
||||
}
|
||||
|
||||
void Patcher::WriteLoadContext() {
|
||||
// This function was called, which modifies X30, so use that as a scratch register.
|
||||
// SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes
|
||||
// of stack.
|
||||
c.STR(X30, SP, 8);
|
||||
c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
|
||||
|
||||
// Load system registers.
|
||||
c.LDR(W0, X30, offsetof(GuestContext, fpsr));
|
||||
c.MSR(oaknut::SystemReg::FPSR, X0);
|
||||
c.LDR(W0, X30, offsetof(GuestContext, fpcr));
|
||||
c.MSR(oaknut::SystemReg::FPCR, X0);
|
||||
c.LDR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
c.MSR(oaknut::SystemReg::NZCV, X0);
|
||||
|
||||
// Load all vector registers.
|
||||
static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
|
||||
for (int i = 0; i <= 30; i += 2) {
|
||||
c.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
|
||||
}
|
||||
|
||||
// Load all general-purpose registers except X30.
|
||||
for (int i = 0; i <= 28; i += 2) {
|
||||
c.LDP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
|
||||
}
|
||||
|
||||
// Reload our return X30 from the stack and return.
|
||||
// The patch code will reload the guest X30 for us.
|
||||
c.LDR(X30, SP, 8);
|
||||
c.RET();
|
||||
}
|
||||
|
||||
void Patcher::WriteSaveContext() {
|
||||
// This function was called, which modifies X30, so use that as a scratch register.
|
||||
// SP contains the guest X30, so save our X30 to SP + 8, since we have allocated 16 bytes of
|
||||
// stack.
|
||||
c.STR(X30, SP, 8);
|
||||
c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
|
||||
|
||||
// Store all general-purpose registers except X30.
|
||||
for (int i = 0; i <= 28; i += 2) {
|
||||
c.STP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
|
||||
}
|
||||
|
||||
// Store all vector registers.
|
||||
static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
|
||||
for (int i = 0; i <= 30; i += 2) {
|
||||
c.STP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
|
||||
}
|
||||
|
||||
// Store guest system registers, X30 and SP, using X0 as a scratch register.
|
||||
c.STR(X0, SP, PRE_INDEXED, -16);
|
||||
c.LDR(X0, SP, 16);
|
||||
c.STR(X0, X30, 8 * 30);
|
||||
c.ADD(X0, SP, 32);
|
||||
c.STR(X0, X30, offsetof(GuestContext, sp));
|
||||
c.MRS(X0, oaknut::SystemReg::FPSR);
|
||||
c.STR(W0, X30, offsetof(GuestContext, fpsr));
|
||||
c.MRS(X0, oaknut::SystemReg::FPCR);
|
||||
c.STR(W0, X30, offsetof(GuestContext, fpcr));
|
||||
c.MRS(X0, oaknut::SystemReg::NZCV);
|
||||
c.STR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
c.LDR(X0, SP, POST_INDEXED, 16);
|
||||
|
||||
// Reload our return X30 from the stack, and return.
|
||||
c.LDR(X30, SP, 8);
|
||||
c.RET();
|
||||
}
|
||||
|
||||
void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
|
||||
// We are about to start saving state, so we need to lock the context.
|
||||
this->LockContext();
|
||||
|
||||
// Store guest X30 to the stack. Then, save the context and restore the stack.
|
||||
// This will save all registers except PC, but we know PC at patch time.
|
||||
c.STR(X30, SP, PRE_INDEXED, -16);
|
||||
c.BL(m_save_context);
|
||||
c.LDR(X30, SP, POST_INDEXED, 16);
|
||||
|
||||
// Now that we've saved all registers, we can use any registers as scratch.
|
||||
// Store PC + 4 to arm interface, since we know the instruction offset from the entry point.
|
||||
oaknut::Label pc_after_svc;
|
||||
c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
c.LDR(X2, pc_after_svc);
|
||||
c.STR(X2, X1, offsetof(GuestContext, pc));
|
||||
|
||||
// Store SVC number to execute when we return
|
||||
c.MOV(X2, svc_id);
|
||||
c.STR(W2, X1, offsetof(GuestContext, svc_swi));
|
||||
|
||||
// We are calling a SVC. Clear esr_el1 and return it.
|
||||
static_assert(std::is_same_v<std::underlying_type_t<HaltReason>, u64>);
|
||||
oaknut::Label retry;
|
||||
c.ADD(X2, X1, offsetof(GuestContext, esr_el1));
|
||||
c.l(retry);
|
||||
c.LDAXR(X0, X2);
|
||||
c.STLXR(W3, XZR, X2);
|
||||
c.CBNZ(W3, retry);
|
||||
|
||||
// Add "calling SVC" flag. Since this is X0, this is now our return value.
|
||||
c.ORR(X0, X0, static_cast<u64>(HaltReason::SupervisorCall));
|
||||
|
||||
// Offset the GuestContext pointer to the HostContext member.
|
||||
// STP has limited range of [-512, 504] which we can't reach otherwise
|
||||
// NB: Due to this all offsets below are from the start of HostContext.
|
||||
c.ADD(X1, X1, offsetof(GuestContext, host_ctx));
|
||||
|
||||
// Reload host TPIDR_EL0 and SP.
|
||||
static_assert(offsetof(HostContext, host_sp) + 8 == offsetof(HostContext, host_tpidr_el0));
|
||||
c.LDP(X2, X3, X1, offsetof(HostContext, host_sp));
|
||||
c.MOV(SP, X2);
|
||||
c.MSR(oaknut::SystemReg::TPIDR_EL0, X3);
|
||||
|
||||
// Load callee-saved host registers and return to host.
|
||||
static constexpr size_t HOST_REGS_OFF = offsetof(HostContext, host_saved_regs);
|
||||
static constexpr size_t HOST_VREGS_OFF = offsetof(HostContext, host_saved_vregs);
|
||||
c.LDP(X19, X20, X1, HOST_REGS_OFF);
|
||||
c.LDP(X21, X22, X1, HOST_REGS_OFF + 2 * sizeof(u64));
|
||||
c.LDP(X23, X24, X1, HOST_REGS_OFF + 4 * sizeof(u64));
|
||||
c.LDP(X25, X26, X1, HOST_REGS_OFF + 6 * sizeof(u64));
|
||||
c.LDP(X27, X28, X1, HOST_REGS_OFF + 8 * sizeof(u64));
|
||||
c.LDP(X29, X30, X1, HOST_REGS_OFF + 10 * sizeof(u64));
|
||||
c.LDP(Q8, Q9, X1, HOST_VREGS_OFF);
|
||||
c.LDP(Q10, Q11, X1, HOST_VREGS_OFF + 2 * sizeof(u128));
|
||||
c.LDP(Q12, Q13, X1, HOST_VREGS_OFF + 4 * sizeof(u128));
|
||||
c.LDP(Q14, Q15, X1, HOST_VREGS_OFF + 6 * sizeof(u128));
|
||||
c.RET();
|
||||
|
||||
// Write the post-SVC trampoline address, which will jump back to the guest after restoring its
|
||||
// state.
|
||||
m_trampolines.push_back({c.offset(), module_dest});
|
||||
|
||||
// Host called this location. Save the return address so we can
|
||||
// unwind the stack properly when jumping back.
|
||||
c.MRS(X2, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context));
|
||||
c.ADD(X0, X2, offsetof(GuestContext, host_ctx));
|
||||
c.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64));
|
||||
|
||||
// Reload all guest registers except X30 and PC.
|
||||
// The function also expects 16 bytes of stack already allocated.
|
||||
c.STR(X30, SP, PRE_INDEXED, -16);
|
||||
c.BL(m_load_context);
|
||||
c.LDR(X30, SP, POST_INDEXED, 16);
|
||||
|
||||
// Use X1 as a scratch register to restore X30.
|
||||
c.STR(X1, SP, PRE_INDEXED, -16);
|
||||
c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
c.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30);
|
||||
c.LDR(X1, SP, POST_INDEXED, 16);
|
||||
|
||||
// Unlock the context.
|
||||
this->UnlockContext();
|
||||
|
||||
// Jump back to the instruction after the emulated SVC.
|
||||
this->BranchToModule(module_dest);
|
||||
|
||||
// Store PC after call.
|
||||
c.l(pc_after_svc);
|
||||
this->WriteModulePc(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
|
||||
oaknut::SystemReg src_reg) {
|
||||
// Retrieve emulated TLS register from GuestContext.
|
||||
c.MRS(dest_reg, oaknut::SystemReg::TPIDR_EL0);
|
||||
if (src_reg == oaknut::SystemReg::TPIDRRO_EL0) {
|
||||
c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0));
|
||||
} else {
|
||||
c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0));
|
||||
}
|
||||
|
||||
// Jump back to the instruction after the emulated MRS.
|
||||
this->BranchToModule(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) {
|
||||
const auto scratch_reg = src_reg.index() == 0 ? X1 : X0;
|
||||
c.STR(scratch_reg, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Save guest value to NativeExecutionParameters::tpidr_el0.
|
||||
c.MRS(scratch_reg, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0));
|
||||
|
||||
// Restore scratch register.
|
||||
c.LDR(scratch_reg, SP, POST_INDEXED, 16);
|
||||
|
||||
// Jump back to the instruction after the emulated MSR.
|
||||
this->BranchToModule(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) {
|
||||
static Common::Arm64::NativeClock clock{};
|
||||
const auto factor = clock.GetGuestCNTFRQFactor();
|
||||
const auto raw_factor = Common::BitCast<std::array<u64, 2>>(factor);
|
||||
|
||||
const auto use_x2_x3 = dest_reg.index() == 0 || dest_reg.index() == 1;
|
||||
oaknut::XReg scratch0 = use_x2_x3 ? X2 : X0;
|
||||
oaknut::XReg scratch1 = use_x2_x3 ? X3 : X1;
|
||||
|
||||
oaknut::Label factorlo;
|
||||
oaknut::Label factorhi;
|
||||
|
||||
// Save scratches.
|
||||
c.STP(scratch0, scratch1, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Load counter value.
|
||||
c.MRS(dest_reg, oaknut::SystemReg::CNTVCT_EL0);
|
||||
|
||||
// Load scaling factor.
|
||||
c.LDR(scratch0, factorlo);
|
||||
c.LDR(scratch1, factorhi);
|
||||
|
||||
// Multiply low bits and get result.
|
||||
c.UMULH(scratch0, dest_reg, scratch0);
|
||||
|
||||
// Multiply high bits and add low bit result.
|
||||
c.MADD(dest_reg, dest_reg, scratch1, scratch0);
|
||||
|
||||
// Reload scratches.
|
||||
c.LDP(scratch0, scratch1, SP, POST_INDEXED, 16);
|
||||
|
||||
// Jump back to the instruction after the emulated MRS.
|
||||
this->BranchToModule(module_dest);
|
||||
|
||||
// Scaling factor constant values.
|
||||
c.l(factorlo);
|
||||
c.dx(raw_factor[0]);
|
||||
c.l(factorhi);
|
||||
c.dx(raw_factor[1]);
|
||||
}
|
||||
|
||||
void Patcher::LockContext() {
|
||||
oaknut::Label retry;
|
||||
|
||||
// Save scratches.
|
||||
c.STP(X0, X1, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Reload lock pointer.
|
||||
c.l(retry);
|
||||
c.CLREX();
|
||||
c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
|
||||
|
||||
static_assert(SpinLockLocked == 0);
|
||||
|
||||
// Load-linked with acquire ordering.
|
||||
c.LDAXR(W1, X0);
|
||||
|
||||
// If the value was SpinLockLocked, clear monitor and retry.
|
||||
c.CBZ(W1, retry);
|
||||
|
||||
// Store-conditional SpinLockLocked with relaxed ordering.
|
||||
c.STXR(W1, WZR, X0);
|
||||
|
||||
// If we failed to store, retry.
|
||||
c.CBNZ(W1, retry);
|
||||
|
||||
// We succeeded! Reload scratches.
|
||||
c.LDP(X0, X1, SP, POST_INDEXED, 16);
|
||||
}
|
||||
|
||||
void Patcher::UnlockContext() {
|
||||
// Save scratches.
|
||||
c.STP(X0, X1, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Load lock pointer.
|
||||
c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
|
||||
|
||||
// Load SpinLockUnlocked.
|
||||
c.MOV(W1, SpinLockUnlocked);
|
||||
|
||||
// Store value with release ordering.
|
||||
c.STLR(W1, X0);
|
||||
|
||||
// Load scratches.
|
||||
c.LDP(X0, X1, SP, POST_INDEXED, 16);
|
||||
}
|
||||
|
||||
} // namespace Core::NCE
|
||||
98
src/core/arm/nce/patch.h
Normal file
98
src/core/arm/nce/patch.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <oaknut/code_block.hpp>
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_typed_address.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
|
||||
namespace Core::NCE {
|
||||
|
||||
enum class PatchMode : u32 {
|
||||
None,
|
||||
PreText, ///< Patch section is inserted before .text
|
||||
PostData, ///< Patch section is inserted after .data
|
||||
};
|
||||
|
||||
using ModuleTextAddress = u64;
|
||||
using PatchTextAddress = u64;
|
||||
using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>;
|
||||
|
||||
class Patcher {
|
||||
public:
|
||||
explicit Patcher();
|
||||
~Patcher();
|
||||
|
||||
void PatchText(const Kernel::PhysicalMemory& program_image,
|
||||
const Kernel::CodeSet::Segment& code);
|
||||
void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
|
||||
Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
|
||||
size_t GetSectionSize() const noexcept;
|
||||
|
||||
[[nodiscard]] PatchMode GetPatchMode() const noexcept {
|
||||
return mode;
|
||||
}
|
||||
|
||||
private:
|
||||
using ModuleDestLabel = uintptr_t;
|
||||
|
||||
struct Trampoline {
|
||||
ptrdiff_t patch_offset;
|
||||
uintptr_t module_offset;
|
||||
};
|
||||
|
||||
void WriteLoadContext();
|
||||
void WriteSaveContext();
|
||||
void LockContext();
|
||||
void UnlockContext();
|
||||
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id);
|
||||
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
|
||||
oaknut::SystemReg src_reg);
|
||||
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg);
|
||||
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg);
|
||||
|
||||
private:
|
||||
void BranchToPatch(uintptr_t module_dest) {
|
||||
m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
|
||||
}
|
||||
|
||||
void BranchToModule(uintptr_t module_dest) {
|
||||
m_branch_to_module_relocations.push_back({c.offset(), module_dest});
|
||||
c.dw(0);
|
||||
}
|
||||
|
||||
void WriteModulePc(uintptr_t module_dest) {
|
||||
m_write_module_pc_relocations.push_back({c.offset(), module_dest});
|
||||
c.dx(0);
|
||||
}
|
||||
|
||||
private:
|
||||
// List of patch instructions we have generated.
|
||||
std::vector<u32> m_patch_instructions{};
|
||||
|
||||
// Relocation type for relative branch from module to patch.
|
||||
struct Relocation {
|
||||
ptrdiff_t patch_offset; ///< Offset in bytes from the start of the patch section.
|
||||
uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
|
||||
};
|
||||
|
||||
oaknut::VectorCodeGenerator c;
|
||||
std::vector<Trampoline> m_trampolines;
|
||||
std::vector<Relocation> m_branch_to_patch_relocations{};
|
||||
std::vector<Relocation> m_branch_to_module_relocations{};
|
||||
std::vector<Relocation> m_write_module_pc_relocations{};
|
||||
std::vector<ModuleTextAddress> m_exclusives{};
|
||||
oaknut::Label m_save_context{};
|
||||
oaknut::Label m_load_context{};
|
||||
PatchMode mode{PatchMode::None};
|
||||
};
|
||||
|
||||
} // namespace Core::NCE
|
||||
@@ -211,6 +211,8 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
||||
system.GPU().ObtainContext();
|
||||
}
|
||||
|
||||
system.ArmInterface(core).Initialize();
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace Core {
|
||||
|
||||
#ifdef ANDROID
|
||||
#ifdef HAS_NCE
|
||||
constexpr size_t VirtualReserveSize = 1ULL << 38;
|
||||
#else
|
||||
constexpr size_t VirtualReserveSize = 1ULL << 39;
|
||||
@@ -15,6 +15,7 @@ constexpr size_t VirtualReserveSize = 1ULL << 39;
|
||||
DeviceMemory::DeviceMemory()
|
||||
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
||||
VirtualReserveSize} {}
|
||||
|
||||
DeviceMemory::~DeviceMemory() = default;
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
|
||||
using ConsoleMotionValues = ConsoleMotionInfo;
|
||||
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
|
||||
|
||||
struct TouchFinger {
|
||||
u64 last_touch{};
|
||||
Common::Point<float> position{};
|
||||
u32 id{};
|
||||
TouchAttribute attribute{};
|
||||
bool pressed{};
|
||||
};
|
||||
|
||||
// Contains all motion related data that is used on the services
|
||||
struct ConsoleMotion {
|
||||
Common::Vec3f accel{};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/thread.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/input_converter.h"
|
||||
#include "core/hle/service/hid/hid_util.h"
|
||||
|
||||
namespace Core::HID {
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
@@ -82,7 +83,7 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde
|
||||
}
|
||||
|
||||
void EmulatedController::ReloadFromSettings() {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
for (std::size_t index = 0; index < player.buttons.size(); ++index) {
|
||||
@@ -118,7 +119,7 @@ void EmulatedController::ReloadFromSettings() {
|
||||
}
|
||||
|
||||
void EmulatedController::ReloadColorsFromSettings() {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
// Avoid updating colors if overridden by physical controller
|
||||
@@ -215,7 +216,7 @@ void EmulatedController::LoadDevices() {
|
||||
}
|
||||
|
||||
void EmulatedController::LoadTASParams() {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
Common::ParamPackage common_params{};
|
||||
common_params.Set("engine", "tas");
|
||||
common_params.Set("port", static_cast<int>(player_index));
|
||||
@@ -243,10 +244,12 @@ void EmulatedController::LoadTASParams() {
|
||||
tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
|
||||
tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
|
||||
tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
|
||||
tas_button_params[Settings::NativeButton::SL].Set("button", 16);
|
||||
tas_button_params[Settings::NativeButton::SR].Set("button", 17);
|
||||
tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
|
||||
tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
|
||||
tas_button_params[Settings::NativeButton::Home].Set("button", 18);
|
||||
tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
|
||||
tas_button_params[Settings::NativeButton::SLRight].Set("button", 20);
|
||||
tas_button_params[Settings::NativeButton::SRRight].Set("button", 21);
|
||||
|
||||
tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
|
||||
tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
|
||||
@@ -262,7 +265,7 @@ void EmulatedController::LoadTASParams() {
|
||||
}
|
||||
|
||||
void EmulatedController::LoadVirtualGamepadParams() {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
Common::ParamPackage common_params{};
|
||||
common_params.Set("engine", "virtual_gamepad");
|
||||
common_params.Set("port", static_cast<int>(player_index));
|
||||
@@ -296,10 +299,12 @@ void EmulatedController::LoadVirtualGamepadParams() {
|
||||
virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
|
||||
virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
|
||||
virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
|
||||
virtual_button_params[Settings::NativeButton::SL].Set("button", 16);
|
||||
virtual_button_params[Settings::NativeButton::SR].Set("button", 17);
|
||||
virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
|
||||
virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
|
||||
virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
|
||||
virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
|
||||
virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20);
|
||||
virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21);
|
||||
|
||||
virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
|
||||
virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
|
||||
@@ -504,9 +509,11 @@ void EmulatedController::ReloadInput() {
|
||||
});
|
||||
}
|
||||
turbo_button_state = 0;
|
||||
is_initalized = true;
|
||||
}
|
||||
|
||||
void EmulatedController::UnloadInput() {
|
||||
is_initalized = false;
|
||||
for (auto& button : button_devices) {
|
||||
button.reset();
|
||||
}
|
||||
@@ -611,7 +618,7 @@ bool EmulatedController::IsConfiguring() const {
|
||||
}
|
||||
|
||||
void EmulatedController::SaveCurrentConfig() {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
auto& player = Settings::values.players.GetValue()[player_index];
|
||||
player.connected = is_connected;
|
||||
player.controller_type = MapNPadToSettingsType(npad_type);
|
||||
@@ -867,12 +874,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
controller.npad_button_state.down.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_down.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SL:
|
||||
case Settings::NativeButton::SLLeft:
|
||||
controller.npad_button_state.left_sl.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SLRight:
|
||||
controller.npad_button_state.right_sl.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SR:
|
||||
case Settings::NativeButton::SRLeft:
|
||||
controller.npad_button_state.left_sr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SRRight:
|
||||
controller.npad_button_state.right_sr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Home:
|
||||
@@ -1198,13 +1209,16 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
|
||||
}
|
||||
|
||||
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
|
||||
|
||||
@@ -1230,9 +1244,13 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
|
||||
}
|
||||
|
||||
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!player.vibration_enabled) {
|
||||
return false;
|
||||
}
|
||||
@@ -1252,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
|
||||
EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
|
||||
LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
|
||||
|
||||
if (!is_initalized) {
|
||||
return Common::Input::DriverResult::InvalidHandle;
|
||||
}
|
||||
|
||||
auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
|
||||
auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& nfc_output_device = output_devices[3];
|
||||
@@ -1297,6 +1319,10 @@ bool EmulatedController::SetCameraFormat(
|
||||
Core::IrSensor::ImageTransferProcessorFormat camera_format) {
|
||||
LOG_INFO(Service_HID, "Set camera format {}", camera_format);
|
||||
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& camera_output_device = output_devices[2];
|
||||
|
||||
@@ -1320,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) {
|
||||
}
|
||||
|
||||
bool EmulatedController::HasNfc() const {
|
||||
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& nfc_output_device = output_devices[3];
|
||||
|
||||
switch (npad_type) {
|
||||
@@ -1357,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() {
|
||||
}
|
||||
|
||||
bool EmulatedController::StartNfcPolling() {
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& nfc_virtual_output_device = output_devices[3];
|
||||
|
||||
@@ -1368,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() {
|
||||
}
|
||||
|
||||
bool EmulatedController::StopNfcPolling() {
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& nfc_virtual_output_device = output_devices[3];
|
||||
|
||||
@@ -1379,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() {
|
||||
}
|
||||
|
||||
bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& nfc_virtual_output_device = output_devices[3];
|
||||
|
||||
@@ -1391,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
|
||||
|
||||
bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
|
||||
Common::Input::MifareRequest& out_data) {
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& nfc_virtual_output_device = output_devices[3];
|
||||
|
||||
@@ -1403,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ
|
||||
}
|
||||
|
||||
bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& nfc_virtual_output_device = output_devices[3];
|
||||
|
||||
@@ -1414,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req
|
||||
}
|
||||
|
||||
bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
|
||||
if (!is_initalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
auto& nfc_virtual_output_device = output_devices[3];
|
||||
|
||||
@@ -1425,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
|
||||
}
|
||||
|
||||
void EmulatedController::SetLedPattern() {
|
||||
if (!is_initalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& device : output_devices) {
|
||||
if (!device) {
|
||||
continue;
|
||||
@@ -1640,7 +1699,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
|
||||
}
|
||||
if (is_connected) {
|
||||
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
|
||||
NpadIdTypeToIndex(npad_id_type));
|
||||
Service::HID::NpadIdTypeToIndex(npad_id_type));
|
||||
}
|
||||
npad_type = npad_type_;
|
||||
}
|
||||
@@ -1890,12 +1949,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const {
|
||||
case Settings::NativeButton::DDown:
|
||||
button_mask.down.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SL:
|
||||
case Settings::NativeButton::SLLeft:
|
||||
button_mask.left_sl.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SLRight:
|
||||
button_mask.right_sl.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SR:
|
||||
case Settings::NativeButton::SRLeft:
|
||||
button_mask.left_sr.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SRRight:
|
||||
button_mask.right_sr.Assign(1);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -559,6 +559,7 @@ private:
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
bool is_configuring{false};
|
||||
bool is_initalized{false};
|
||||
bool system_buttons_enabled{true};
|
||||
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
|
||||
u32 turbo_button_state{0};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/emulated_devices.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/hid_util.h"
|
||||
|
||||
namespace Core::HID {
|
||||
|
||||
@@ -98,11 +99,11 @@ const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
|
||||
}
|
||||
|
||||
EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
|
||||
return GetEmulatedController(IndexToNpadIdType(index));
|
||||
return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
|
||||
}
|
||||
|
||||
const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
|
||||
return GetEmulatedController(IndexToNpadIdType(index));
|
||||
return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
|
||||
}
|
||||
|
||||
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/point.h"
|
||||
#include "common/uuid.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
namespace Core::HID {
|
||||
|
||||
@@ -218,6 +219,13 @@ enum class NpadIdType : u32 {
|
||||
Invalid = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
enum class NpadInterfaceType : u8 {
|
||||
Bluetooth = 1,
|
||||
Rail = 2,
|
||||
Usb = 3,
|
||||
Embedded = 4,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadStyleIndex
|
||||
enum class NpadStyleIndex : u8 {
|
||||
None = 0,
|
||||
@@ -356,6 +364,14 @@ struct TouchState {
|
||||
};
|
||||
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
|
||||
|
||||
struct TouchFinger {
|
||||
u64 last_touch{};
|
||||
Common::Point<float> position{};
|
||||
u32 id{};
|
||||
TouchAttribute attribute{};
|
||||
bool pressed{};
|
||||
};
|
||||
|
||||
// This is nn::hid::TouchScreenConfigurationForNx
|
||||
struct TouchScreenConfigurationForNx {
|
||||
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
|
||||
@@ -583,6 +599,29 @@ struct SixAxisSensorIcInformation {
|
||||
static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
|
||||
"SixAxisSensorIcInformation is an invalid size");
|
||||
|
||||
// This is nn::hid::SixAxisSensorAttribute
|
||||
struct SixAxisSensorAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_interpolated;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::SixAxisSensorState
|
||||
struct SixAxisSensorState {
|
||||
s64 delta_time{};
|
||||
s64 sampling_number{};
|
||||
Common::Vec3f accel{};
|
||||
Common::Vec3f gyro{};
|
||||
Common::Vec3f rotation{};
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
SixAxisSensorAttribute attribute{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
|
||||
|
||||
// This is nn::hid::VibrationDeviceHandle
|
||||
struct VibrationDeviceHandle {
|
||||
NpadStyleIndex npad_type{NpadStyleIndex::None};
|
||||
@@ -693,60 +732,4 @@ struct UniquePadId {
|
||||
};
|
||||
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
|
||||
|
||||
/// Converts a NpadIdType to an array index.
|
||||
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
|
||||
switch (npad_id_type) {
|
||||
case NpadIdType::Player1:
|
||||
return 0;
|
||||
case NpadIdType::Player2:
|
||||
return 1;
|
||||
case NpadIdType::Player3:
|
||||
return 2;
|
||||
case NpadIdType::Player4:
|
||||
return 3;
|
||||
case NpadIdType::Player5:
|
||||
return 4;
|
||||
case NpadIdType::Player6:
|
||||
return 5;
|
||||
case NpadIdType::Player7:
|
||||
return 6;
|
||||
case NpadIdType::Player8:
|
||||
return 7;
|
||||
case NpadIdType::Handheld:
|
||||
return 8;
|
||||
case NpadIdType::Other:
|
||||
return 9;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an array index to a NpadIdType
|
||||
constexpr NpadIdType IndexToNpadIdType(size_t index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return NpadIdType::Player1;
|
||||
case 1:
|
||||
return NpadIdType::Player2;
|
||||
case 2:
|
||||
return NpadIdType::Player3;
|
||||
case 3:
|
||||
return NpadIdType::Player4;
|
||||
case 4:
|
||||
return NpadIdType::Player5;
|
||||
case 5:
|
||||
return NpadIdType::Player6;
|
||||
case 6:
|
||||
return NpadIdType::Player7;
|
||||
case 7:
|
||||
return NpadIdType::Player8;
|
||||
case 8:
|
||||
return NpadIdType::Handheld;
|
||||
case 9:
|
||||
return NpadIdType::Other;
|
||||
default:
|
||||
return NpadIdType::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core::HID
|
||||
|
||||
@@ -5,21 +5,22 @@
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hid/input_interpreter.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/hid/hid_server.h"
|
||||
#include "core/hle/service/hid/resource_manager.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
InputInterpreter::InputInterpreter(Core::System& system)
|
||||
: npad{system.ServiceManager()
|
||||
.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
|
||||
.GetService<Service::HID::IHidServer>("hid")
|
||||
->GetResourceManager()
|
||||
->GetNpad()} {
|
||||
ResetButtonStates();
|
||||
}
|
||||
|
||||
InputInterpreter::~InputInterpreter() = default;
|
||||
|
||||
void InputInterpreter::PollInput() {
|
||||
const auto button_state = npad.GetAndResetPressState();
|
||||
const auto button_state = npad->GetAndResetPressState();
|
||||
|
||||
previous_index = current_index;
|
||||
current_index = (current_index + 1) % button_states.size();
|
||||
|
||||
@@ -16,7 +16,7 @@ enum class NpadButton : u64;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_NPad;
|
||||
class NPad;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Service::HID::Controller_NPad& npad;
|
||||
std::shared_ptr<Service::HID::NPad> npad;
|
||||
|
||||
/// Stores 9 consecutive button states polled from HID.
|
||||
std::array<Core::HID::NpadButton, 9> button_states{};
|
||||
|
||||
@@ -75,12 +75,26 @@ struct CodeSet final {
|
||||
return segments[2];
|
||||
}
|
||||
|
||||
#ifdef HAS_NCE
|
||||
Segment& PatchSegment() {
|
||||
return patch_segment;
|
||||
}
|
||||
|
||||
const Segment& PatchSegment() const {
|
||||
return patch_segment;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// The overall data that backs this code set.
|
||||
Kernel::PhysicalMemory memory;
|
||||
|
||||
/// The segments that comprise this code set.
|
||||
std::array<Segment, 3> segments;
|
||||
|
||||
#ifdef HAS_NCE
|
||||
Segment patch_segment;
|
||||
#endif
|
||||
|
||||
/// The entry point address for this code set.
|
||||
KProcessAddress entrypoint = 0;
|
||||
};
|
||||
|
||||
@@ -25,8 +25,8 @@ constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
|
||||
{ .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
|
||||
{ .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
|
||||
#ifdef ANDROID
|
||||
// With Android, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region.
|
||||
#ifdef HAS_NCE
|
||||
// With NCE, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region.
|
||||
{ .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
|
||||
#else
|
||||
{ .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
|
||||
|
||||
@@ -88,6 +88,22 @@ Result FlushDataCache(AddressType addr, u64 size) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr Common::MemoryPermission ConvertToMemoryPermission(KMemoryPermission perm) {
|
||||
Common::MemoryPermission perms{};
|
||||
if (True(perm & KMemoryPermission::UserRead)) {
|
||||
perms |= Common::MemoryPermission::Read;
|
||||
}
|
||||
if (True(perm & KMemoryPermission::UserWrite)) {
|
||||
perms |= Common::MemoryPermission::Write;
|
||||
}
|
||||
#ifdef HAS_NCE
|
||||
if (True(perm & KMemoryPermission::UserExecute)) {
|
||||
perms |= Common::MemoryPermission::Execute;
|
||||
}
|
||||
#endif
|
||||
return perms;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void KPageTableBase::MemoryRange::Open() {
|
||||
@@ -170,7 +186,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
|
||||
KMemoryManager::Pool pool, KProcessAddress code_address,
|
||||
size_t code_size, KSystemResource* system_resource,
|
||||
KResourceLimit* resource_limit,
|
||||
Core::Memory::Memory& memory) {
|
||||
Core::Memory::Memory& memory,
|
||||
KProcessAddress aslr_space_start) {
|
||||
// Calculate region extents.
|
||||
const size_t as_width = GetAddressSpaceWidth(as_type);
|
||||
const KProcessAddress start = 0;
|
||||
@@ -211,7 +228,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
|
||||
heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
|
||||
stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
|
||||
kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
|
||||
m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
|
||||
m_code_region_start = m_address_space_start + aslr_space_start +
|
||||
GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
|
||||
m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
|
||||
m_alias_code_region_start = m_code_region_start;
|
||||
m_alias_code_region_end = m_code_region_end;
|
||||
@@ -5643,7 +5661,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||
case OperationType::Map: {
|
||||
ASSERT(virt_addr != 0);
|
||||
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
|
||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr);
|
||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
|
||||
ConvertToMemoryPermission(properties.perm));
|
||||
|
||||
// Open references to pages, if we should.
|
||||
if (this->IsHeapPhysicalAddress(phys_addr)) {
|
||||
@@ -5658,8 +5677,11 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||
}
|
||||
case OperationType::ChangePermissions:
|
||||
case OperationType::ChangePermissionsAndRefresh:
|
||||
case OperationType::ChangePermissionsAndRefreshAndFlush:
|
||||
case OperationType::ChangePermissionsAndRefreshAndFlush: {
|
||||
m_memory->ProtectRegion(*m_impl, virt_addr, num_pages * PageSize,
|
||||
ConvertToMemoryPermission(properties.perm));
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -5687,7 +5709,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||
const size_t size{node.GetNumPages() * PageSize};
|
||||
|
||||
// Map the pages.
|
||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress());
|
||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
|
||||
ConvertToMemoryPermission(properties.perm));
|
||||
|
||||
virt_addr += size;
|
||||
}
|
||||
|
||||
@@ -235,7 +235,8 @@ public:
|
||||
bool enable_device_address_space_merge, bool from_back,
|
||||
KMemoryManager::Pool pool, KProcessAddress code_address,
|
||||
size_t code_size, KSystemResource* system_resource,
|
||||
KResourceLimit* resource_limit, Core::Memory::Memory& memory);
|
||||
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
|
||||
KProcessAddress aslr_space_start);
|
||||
|
||||
void Finalize();
|
||||
|
||||
|
||||
@@ -300,7 +300,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
|
||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||
params.code_address, params.code_num_pages * PageSize,
|
||||
m_system_resource, res_limit, this->GetMemory()));
|
||||
m_system_resource, res_limit, this->GetMemory(), 0));
|
||||
}
|
||||
ON_RESULT_FAILURE_2 {
|
||||
m_page_table.Finalize();
|
||||
@@ -332,7 +332,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
|
||||
|
||||
Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
|
||||
std::span<const u32> user_caps, KResourceLimit* res_limit,
|
||||
KMemoryManager::Pool pool) {
|
||||
KMemoryManager::Pool pool, KProcessAddress aslr_space_start) {
|
||||
ASSERT(res_limit != nullptr);
|
||||
|
||||
// Set members.
|
||||
@@ -393,7 +393,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
|
||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||
params.code_address, code_size, m_system_resource, res_limit,
|
||||
this->GetMemory()));
|
||||
this->GetMemory(), aslr_space_start));
|
||||
}
|
||||
ON_RESULT_FAILURE_2 {
|
||||
m_page_table.Finalize();
|
||||
@@ -1128,7 +1128,7 @@ KProcess::KProcess(KernelCore& kernel)
|
||||
KProcess::~KProcess() = default;
|
||||
|
||||
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
||||
bool is_hbl) {
|
||||
KProcessAddress aslr_space_start, bool is_hbl) {
|
||||
// Create a resource limit for the process.
|
||||
const auto physical_memory_size =
|
||||
m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
|
||||
@@ -1179,7 +1179,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
.name = {},
|
||||
.version = {},
|
||||
.program_id = metadata.GetTitleID(),
|
||||
.code_address = code_address,
|
||||
.code_address = code_address + GetInteger(aslr_space_start),
|
||||
.code_num_pages = static_cast<s32>(code_size / PageSize),
|
||||
.flags = flag,
|
||||
.reslimit = Svc::InvalidHandle,
|
||||
@@ -1193,7 +1193,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
|
||||
// Initialize for application process.
|
||||
R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit,
|
||||
KMemoryManager::Pool::Application));
|
||||
KMemoryManager::Pool::Application, aslr_space_start));
|
||||
|
||||
// Assign remaining properties.
|
||||
m_is_hbl = is_hbl;
|
||||
@@ -1214,6 +1214,17 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
|
||||
ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute);
|
||||
ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read);
|
||||
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
if (Settings::IsNceEnabled()) {
|
||||
auto& buffer = m_kernel.System().DeviceMemory().buffer;
|
||||
const auto& code = code_set.CodeSegment();
|
||||
const auto& patch = code_set.PatchSegment();
|
||||
buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true);
|
||||
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true);
|
||||
ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) {
|
||||
|
||||
@@ -120,6 +120,9 @@ private:
|
||||
std::atomic<s64> m_num_ipc_messages{};
|
||||
std::atomic<s64> m_num_ipc_replies{};
|
||||
std::atomic<s64> m_num_ipc_receives{};
|
||||
#ifdef HAS_NCE
|
||||
std::unordered_map<u64, u64> m_post_handlers{};
|
||||
#endif
|
||||
|
||||
private:
|
||||
Result StartTermination();
|
||||
@@ -150,7 +153,8 @@ public:
|
||||
std::span<const u32> caps, KResourceLimit* res_limit,
|
||||
KMemoryManager::Pool pool, bool immortal);
|
||||
Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps,
|
||||
KResourceLimit* res_limit, KMemoryManager::Pool pool);
|
||||
KResourceLimit* res_limit, KMemoryManager::Pool pool,
|
||||
KProcessAddress aslr_space_start);
|
||||
void Exit();
|
||||
|
||||
const char* GetName() const {
|
||||
@@ -466,6 +470,12 @@ public:
|
||||
|
||||
static void Switch(KProcess* cur_process, KProcess* next_process);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
std::unordered_map<u64, u64>& GetPostHandlers() noexcept {
|
||||
return m_post_handlers;
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
|
||||
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
|
||||
@@ -479,7 +489,7 @@ public:
|
||||
|
||||
public:
|
||||
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
||||
bool is_hbl);
|
||||
KProcessAddress aslr_space_start, bool is_hbl);
|
||||
|
||||
void LoadModule(CodeSet code_set, KProcessAddress base_addr);
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@ public:
|
||||
Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge,
|
||||
bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address,
|
||||
size_t code_size, KSystemResource* system_resource,
|
||||
KResourceLimit* resource_limit, Core::Memory::Memory& memory) {
|
||||
R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge,
|
||||
from_back, pool, code_address, code_size,
|
||||
system_resource, resource_limit, memory));
|
||||
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
|
||||
KProcessAddress aslr_space_start) {
|
||||
R_RETURN(m_page_table.InitializeForProcess(
|
||||
as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size,
|
||||
system_resource, resource_limit, memory, aslr_space_start));
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
|
||||
@@ -655,6 +655,21 @@ public:
|
||||
return m_stack_top;
|
||||
}
|
||||
|
||||
public:
|
||||
// TODO: This shouldn't be defined in kernel namespace
|
||||
struct NativeExecutionParameters {
|
||||
u64 tpidr_el0{};
|
||||
u64 tpidrro_el0{};
|
||||
void* native_context{};
|
||||
std::atomic<u32> lock{1};
|
||||
bool is_running{};
|
||||
u32 magic{Common::MakeMagic('Y', 'U', 'Z', 'U')};
|
||||
};
|
||||
|
||||
NativeExecutionParameters& GetNativeExecutionParameters() {
|
||||
return m_native_execution_parameters;
|
||||
}
|
||||
|
||||
private:
|
||||
KThread* RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key,
|
||||
bool is_kernel_address_key);
|
||||
@@ -914,6 +929,7 @@ private:
|
||||
ThreadWaitReasonForDebugging m_wait_reason_for_debugging{};
|
||||
uintptr_t m_argument{};
|
||||
KProcessAddress m_stack_top{};
|
||||
NativeExecutionParameters m_native_execution_parameters{};
|
||||
|
||||
public:
|
||||
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#ifdef HAS_NCE
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
#endif
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -14,7 +18,8 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu
|
||||
: m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} {
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
|
||||
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
|
||||
// an NCE interface or a 32-bit instance of Dynarmic. This should be abstracted out to a CPU
|
||||
// manager.
|
||||
auto& kernel = system.Kernel();
|
||||
m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
|
||||
system, kernel.IsMulticore(),
|
||||
@@ -28,6 +33,13 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu
|
||||
PhysicalCore::~PhysicalCore() = default;
|
||||
|
||||
void PhysicalCore::Initialize(bool is_64_bit) {
|
||||
#if defined(HAS_NCE)
|
||||
if (Settings::IsNceEnabled()) {
|
||||
m_arm_interface = std::make_unique<Core::ARM_NCE>(m_system, m_system.Kernel().IsMulticore(),
|
||||
m_core_index);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
auto& kernel = m_system.Kernel();
|
||||
if (!is_64_bit) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/am/applets/applet_cabinet.h"
|
||||
#include "core/hle/service/am/applets/applet_controller.h"
|
||||
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
|
||||
#include "core/hle/service/am/applets/applet_profile_select.h"
|
||||
#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
|
||||
@@ -35,6 +37,7 @@
|
||||
#include "core/hle/service/caps/caps_su.h"
|
||||
#include "core/hle/service/caps/caps_types.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||
@@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_)
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateWindow"},
|
||||
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
|
||||
{2, nullptr, "GetAppletResourceUserIdOfCallerApplet"},
|
||||
{2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
|
||||
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
|
||||
{11, nullptr, "ReleaseForegroundRights"},
|
||||
{12, nullptr, "RejectToChangeIntoBackground"},
|
||||
@@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
|
||||
rb.Push<u64>(process_id);
|
||||
}
|
||||
|
||||
void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
|
||||
const u64 process_id = 0;
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u64>(process_id);
|
||||
}
|
||||
|
||||
void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
|
||||
{6, nullptr, "GetPopInteractiveInDataEvent"},
|
||||
{10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
|
||||
{11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
|
||||
{12, nullptr, "GetMainAppletIdentityInfo"},
|
||||
{12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
|
||||
{13, nullptr, "CanUseApplicationCore"},
|
||||
{14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
|
||||
{15, nullptr, "GetMainAppletApplicationControlProperty"},
|
||||
@@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
|
||||
case Applets::AppletId::SoftwareKeyboard:
|
||||
PushInShowSoftwareKeyboard();
|
||||
break;
|
||||
case Applets::AppletId::Controller:
|
||||
PushInShowController();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
|
||||
rb.PushRaw(applet_info);
|
||||
}
|
||||
|
||||
void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
|
||||
struct AppletIdentityInfo {
|
||||
Applets::AppletId applet_id;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u64 application_id;
|
||||
};
|
||||
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
const AppletIdentityInfo applet_info{
|
||||
.applet_id = Applets::AppletId::QLaunch,
|
||||
.application_id = 0x0100000000001000ull,
|
||||
};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(applet_info);
|
||||
}
|
||||
|
||||
void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
|
||||
struct AppletIdentityInfo {
|
||||
Applets::AppletId applet_id;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u64 application_id;
|
||||
};
|
||||
|
||||
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
const AppletIdentityInfo applet_info{
|
||||
@@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() {
|
||||
queue_data.emplace_back(std::move(settings_data));
|
||||
}
|
||||
|
||||
void ILibraryAppletSelfAccessor::PushInShowController() {
|
||||
const Applets::CommonArguments common_args = {
|
||||
.arguments_version = Applets::CommonArgumentVersion::Version3,
|
||||
.size = Applets::CommonArgumentSize::Version3,
|
||||
.library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
|
||||
.theme_color = Applets::ThemeColor::BasicBlack,
|
||||
.play_startup_sound = true,
|
||||
.system_tick = system.CoreTiming().GetClockTicks(),
|
||||
};
|
||||
|
||||
Applets::ControllerSupportArgNew user_args = {
|
||||
.header = {.player_count_min = 1,
|
||||
.player_count_max = 4,
|
||||
.enable_take_over_connection = true,
|
||||
.enable_left_justify = false,
|
||||
.enable_permit_joy_dual = true,
|
||||
.enable_single_mode = false,
|
||||
.enable_identification_color = false},
|
||||
.identification_colors = {},
|
||||
.enable_explain_text = false,
|
||||
.explain_text = {},
|
||||
};
|
||||
|
||||
Applets::ControllerSupportArgPrivate private_args = {
|
||||
.arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
|
||||
.arg_size = sizeof(Applets::ControllerSupportArgNew),
|
||||
.is_home_menu = true,
|
||||
.flag_1 = true,
|
||||
.mode = Applets::ControllerSupportMode::ShowControllerSupport,
|
||||
.caller = Applets::ControllerSupportCaller::
|
||||
Application, // switchbrew: Always zero except with
|
||||
// ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
|
||||
// which sets this to the input param
|
||||
.style_set = Core::HID::NpadStyleSet::None,
|
||||
.joy_hold_type = 0,
|
||||
};
|
||||
std::vector<u8> common_args_data(sizeof(common_args));
|
||||
std::vector<u8> private_args_data(sizeof(private_args));
|
||||
std::vector<u8> user_args_data(sizeof(user_args));
|
||||
|
||||
std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
|
||||
std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
|
||||
std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
|
||||
|
||||
queue_data.emplace_back(std::move(common_args_data));
|
||||
queue_data.emplace_back(std::move(private_args_data));
|
||||
queue_data.emplace_back(std::move(user_args_data));
|
||||
}
|
||||
|
||||
void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
|
||||
const Applets::CommonArguments arguments{
|
||||
.arguments_version = Applets::CommonArgumentVersion::Version3,
|
||||
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
|
||||
private:
|
||||
void GetAppletResourceUserId(HLERequestContext& ctx);
|
||||
void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
|
||||
void AcquireForegroundRights(HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
@@ -345,6 +346,7 @@ private:
|
||||
void PopInData(HLERequestContext& ctx);
|
||||
void PushOutData(HLERequestContext& ctx);
|
||||
void GetLibraryAppletInfo(HLERequestContext& ctx);
|
||||
void GetMainAppletIdentityInfo(HLERequestContext& ctx);
|
||||
void ExitProcessAndReturn(HLERequestContext& ctx);
|
||||
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
|
||||
void GetDesirableKeyboardLayout(HLERequestContext& ctx);
|
||||
@@ -355,6 +357,7 @@ private:
|
||||
void PushInShowCabinetData();
|
||||
void PushInShowMiiEditData();
|
||||
void PushInShowSoftwareKeyboard();
|
||||
void PushInShowController();
|
||||
|
||||
std::deque<std::vector<u8>> queue_data;
|
||||
};
|
||||
|
||||
@@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
|
||||
Service::NFP::RegisterInfoPrivate register_info{};
|
||||
std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
|
||||
std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
|
||||
|
||||
register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
|
||||
register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'});
|
||||
nfp_device->SetRegisterInfoPrivate(register_info);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 {
|
||||
struct ControllerSupportArgPrivate {
|
||||
u32 arg_private_size{};
|
||||
u32 arg_size{};
|
||||
bool flag_0{};
|
||||
bool is_home_menu{};
|
||||
bool flag_1{};
|
||||
ControllerSupportMode mode{};
|
||||
ControllerSupportCaller caller{};
|
||||
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
|
||||
private:
|
||||
void GetCore(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
LOG_WARNING(Service_BTM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -263,13 +263,13 @@ public:
|
||||
explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "StartGamepadPairing"},
|
||||
{1, nullptr, "CancelGamepadPairing"},
|
||||
{0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
|
||||
{1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
|
||||
{2, nullptr, "ClearGamepadPairingDatabase"},
|
||||
{3, nullptr, "GetPairedGamepadCount"},
|
||||
{4, nullptr, "EnableRadio"},
|
||||
{5, nullptr, "DisableRadio"},
|
||||
{6, nullptr, "GetRadioOnOff"},
|
||||
{6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
|
||||
{7, nullptr, "AcquireRadioEvent"},
|
||||
{8, nullptr, "AcquireGamepadPairingEvent"},
|
||||
{9, nullptr, "IsGamepadPairingStarted"},
|
||||
@@ -280,18 +280,58 @@ public:
|
||||
{14, nullptr, "AcquireAudioDeviceConnectionEvent"},
|
||||
{15, nullptr, "ConnectAudioDevice"},
|
||||
{16, nullptr, "IsConnectingAudioDevice"},
|
||||
{17, nullptr, "GetConnectedAudioDevices"},
|
||||
{17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
|
||||
{18, nullptr, "DisconnectAudioDevice"},
|
||||
{19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
|
||||
{20, nullptr, "GetPairedAudioDevices"},
|
||||
{21, nullptr, "RemoveAudioDevicePairing"},
|
||||
{22, nullptr, "RequestAudioDeviceConnectionRejection"},
|
||||
{23, nullptr, "CancelAudioDeviceConnectionRejection"}
|
||||
{22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
|
||||
{23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void IsRadioEnabled(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
void StartGamepadPairing(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void CancelGamepadPairing(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetConnectedAudioDevices(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
class BTM_SYS final : public ServiceFramework<BTM_SYS> {
|
||||
@@ -308,7 +348,7 @@ public:
|
||||
|
||||
private:
|
||||
void GetCore(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
LOG_WARNING(Service_BTM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
{10200, nullptr, "SendFriendRequestForApplication"},
|
||||
{10211, nullptr, "AddFacedFriendRequestForApplication"},
|
||||
{10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
|
||||
{10420, nullptr, "IsBlockedUserListCacheAvailable"},
|
||||
{10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
|
||||
{10421, nullptr, "EnsureBlockedUserListAvailable"},
|
||||
{10500, nullptr, "GetProfileList"},
|
||||
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
|
||||
@@ -206,6 +206,17 @@ private:
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
void CheckBlockedUserListAvailability(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* completion_event;
|
||||
|
||||
42
src/core/hle/service/hid/controllers/console_six_axis.cpp
Normal file
42
src/core/hle/service/hid/controllers/console_six_axis.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/console_six_axis.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
|
||||
|
||||
ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
console = hid_core.GetEmulatedConsole();
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
|
||||
"ConsoleSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
}
|
||||
|
||||
ConsoleSixAxis::~ConsoleSixAxis() = default;
|
||||
|
||||
void ConsoleSixAxis::OnInit() {}
|
||||
|
||||
void ConsoleSixAxis::OnRelease() {}
|
||||
|
||||
void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto motion_status = console->GetMotion();
|
||||
|
||||
shared_memory->sampling_number++;
|
||||
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
shared_memory->verticalization_error = motion_status.verticalization_error;
|
||||
shared_memory->gyro_bias = motion_status.gyro_bias;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
43
src/core/hle/service/hid/controllers/console_six_axis.h
Normal file
43
src/core/hle/service/hid/controllers/console_six_axis.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/vector_math.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedConsole;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class ConsoleSixAxis final : public ControllerBase {
|
||||
public:
|
||||
explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~ConsoleSixAxis() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
// When the controller is released
|
||||
void OnRelease() override;
|
||||
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
private:
|
||||
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
|
||||
struct ConsoleSharedMemory {
|
||||
u64 sampling_number{};
|
||||
bool is_seven_six_axis_sensor_at_rest{};
|
||||
INSERT_PADDING_BYTES(3); // padding
|
||||
f32 verticalization_error{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
};
|
||||
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
|
||||
|
||||
ConsoleSharedMemory* shared_memory = nullptr;
|
||||
Core::HID::EmulatedConsole* console = nullptr;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -8,12 +8,17 @@ namespace Service::HID {
|
||||
ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
|
||||
ControllerBase::~ControllerBase() = default;
|
||||
|
||||
void ControllerBase::ActivateController() {
|
||||
Result ControllerBase::Activate() {
|
||||
if (is_activated) {
|
||||
return;
|
||||
return ResultSuccess;
|
||||
}
|
||||
is_activated = true;
|
||||
OnInit();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ControllerBase::Activate(u64 aruid) {
|
||||
return Activate();
|
||||
}
|
||||
|
||||
void ControllerBase::DeactivateController() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
@@ -31,7 +32,8 @@ public:
|
||||
// When the controller is requesting a motion update for the shared memory
|
||||
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
|
||||
|
||||
void ActivateController();
|
||||
Result Activate();
|
||||
Result Activate(u64 aruid);
|
||||
|
||||
void DeactivateController();
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
|
||||
|
||||
Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
|
||||
"DebugPadSharedMemory is bigger than the shared memory");
|
||||
@@ -22,13 +22,13 @@ Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_
|
||||
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
|
||||
}
|
||||
|
||||
Controller_DebugPad::~Controller_DebugPad() = default;
|
||||
DebugPad::~DebugPad() = default;
|
||||
|
||||
void Controller_DebugPad::OnInit() {}
|
||||
void DebugPad::OnInit() {}
|
||||
|
||||
void Controller_DebugPad::OnRelease() {}
|
||||
void DebugPad::OnRelease() {}
|
||||
|
||||
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->debug_pad_lifo.buffer_count = 0;
|
||||
shared_memory->debug_pad_lifo.buffer_tail = 0;
|
||||
|
||||
@@ -15,10 +15,10 @@ struct AnalogStickState;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_DebugPad final : public ControllerBase {
|
||||
class DebugPad final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Controller_DebugPad() override;
|
||||
explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~DebugPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -23,7 +23,7 @@ constexpr f32 Square(s32 num) {
|
||||
return static_cast<f32>(num * num);
|
||||
}
|
||||
|
||||
Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase(hid_core_) {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
|
||||
"GestureSharedMemory is bigger than the shared memory");
|
||||
@@ -31,17 +31,17 @@ Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_sh
|
||||
reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
console = hid_core.GetEmulatedConsole();
|
||||
}
|
||||
Controller_Gesture::~Controller_Gesture() = default;
|
||||
Gesture::~Gesture() = default;
|
||||
|
||||
void Controller_Gesture::OnInit() {
|
||||
void Gesture::OnInit() {
|
||||
shared_memory->gesture_lifo.buffer_count = 0;
|
||||
shared_memory->gesture_lifo.buffer_tail = 0;
|
||||
force_update = true;
|
||||
}
|
||||
|
||||
void Controller_Gesture::OnRelease() {}
|
||||
void Gesture::OnRelease() {}
|
||||
|
||||
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->gesture_lifo.buffer_count = 0;
|
||||
shared_memory->gesture_lifo.buffer_tail = 0;
|
||||
@@ -64,7 +64,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
UpdateGestureSharedMemory(gesture, time_difference);
|
||||
}
|
||||
|
||||
void Controller_Gesture::ReadTouchInput() {
|
||||
void Gesture::ReadTouchInput() {
|
||||
if (!Settings::values.touchscreen.enabled) {
|
||||
fingers = {};
|
||||
return;
|
||||
@@ -76,8 +76,7 @@ void Controller_Gesture::ReadTouchInput() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
|
||||
f32 time_difference) {
|
||||
bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
if (force_update) {
|
||||
force_update = false;
|
||||
@@ -100,8 +99,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
|
||||
return false;
|
||||
}
|
||||
|
||||
void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
|
||||
f32 time_difference) {
|
||||
void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
|
||||
GestureType type = GestureType::Idle;
|
||||
GestureAttribute attributes{};
|
||||
|
||||
@@ -138,8 +136,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
|
||||
shared_memory->gesture_lifo.WriteNextEntry(next_state);
|
||||
}
|
||||
|
||||
void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
|
||||
GestureAttribute& attributes) {
|
||||
void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
|
||||
GestureAttribute& attributes) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
gesture.detection_count++;
|
||||
@@ -152,8 +150,8 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& typ
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
|
||||
f32 time_difference) {
|
||||
void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
|
||||
f32 time_difference) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
// Promote to pan type if touch moved
|
||||
@@ -186,9 +184,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Gestu
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::EndGesture(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
GestureAttribute& attributes, f32 time_difference) {
|
||||
void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
GestureType& type, GestureAttribute& attributes, f32 time_difference) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
if (last_gesture_props.active_points != 0) {
|
||||
@@ -222,9 +219,8 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
GestureAttribute& attributes) {
|
||||
void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
GestureType& type, GestureAttribute& attributes) {
|
||||
type = GestureType::Tap;
|
||||
gesture = last_gesture_props;
|
||||
force_update = true;
|
||||
@@ -236,9 +232,8 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
f32 time_difference) {
|
||||
void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
GestureType& type, f32 time_difference) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
next_state.delta = gesture.mid_point - last_entry.pos;
|
||||
@@ -263,9 +258,8 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, GestureType& type,
|
||||
f32 time_difference) {
|
||||
void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
GestureType& type, f32 time_difference) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
next_state.vel_x =
|
||||
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
|
||||
@@ -287,8 +281,8 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
|
||||
force_update = true;
|
||||
}
|
||||
|
||||
void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
|
||||
GestureProperties& last_gesture_props, GestureType& type) {
|
||||
void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
|
||||
GestureType& type) {
|
||||
const auto& last_entry = GetLastGestureEntry();
|
||||
|
||||
type = GestureType::Swipe;
|
||||
@@ -311,11 +305,11 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
|
||||
next_state.direction = GestureDirection::Up;
|
||||
}
|
||||
|
||||
const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
|
||||
const Gesture::GestureState& Gesture::GetLastGestureEntry() const {
|
||||
return shared_memory->gesture_lifo.ReadCurrentEntry().state;
|
||||
}
|
||||
|
||||
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
|
||||
Gesture::GestureProperties Gesture::GetGestureProperties() {
|
||||
GestureProperties gesture;
|
||||
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
|
||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Gesture final : public ControllerBase {
|
||||
class Gesture final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Controller_Gesture() override;
|
||||
explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Gesture() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
|
||||
|
||||
Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
|
||||
"KeyboardSharedMemory is bigger than the shared memory");
|
||||
@@ -21,13 +21,13 @@ Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_
|
||||
emulated_devices = hid_core.GetEmulatedDevices();
|
||||
}
|
||||
|
||||
Controller_Keyboard::~Controller_Keyboard() = default;
|
||||
Keyboard::~Keyboard() = default;
|
||||
|
||||
void Controller_Keyboard::OnInit() {}
|
||||
void Keyboard::OnInit() {}
|
||||
|
||||
void Controller_Keyboard::OnRelease() {}
|
||||
void Keyboard::OnRelease() {}
|
||||
|
||||
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->keyboard_lifo.buffer_count = 0;
|
||||
shared_memory->keyboard_lifo.buffer_tail = 0;
|
||||
|
||||
@@ -14,10 +14,10 @@ struct KeyboardKey;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Keyboard final : public ControllerBase {
|
||||
class Keyboard final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Controller_Keyboard() override;
|
||||
explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Keyboard() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
|
||||
|
||||
Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
|
||||
"MouseSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
@@ -21,12 +20,12 @@ Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared
|
||||
emulated_devices = hid_core.GetEmulatedDevices();
|
||||
}
|
||||
|
||||
Controller_Mouse::~Controller_Mouse() = default;
|
||||
Mouse::~Mouse() = default;
|
||||
|
||||
void Controller_Mouse::OnInit() {}
|
||||
void Controller_Mouse::OnRelease() {}
|
||||
void Mouse::OnInit() {}
|
||||
void Mouse::OnRelease() {}
|
||||
|
||||
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->mouse_lifo.buffer_count = 0;
|
||||
shared_memory->mouse_lifo.buffer_tail = 0;
|
||||
|
||||
@@ -14,10 +14,10 @@ struct AnalogStickState;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Mouse final : public ControllerBase {
|
||||
class Mouse final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Controller_Mouse() override;
|
||||
explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Mouse() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
#include "core/hle/service/hid/hid_util.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Service::HID {
|
||||
@@ -29,60 +30,8 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
|
||||
Core::HID::NpadIdType::Handheld,
|
||||
};
|
||||
|
||||
bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
|
||||
switch (npad_id) {
|
||||
case Core::HID::NpadIdType::Player1:
|
||||
case Core::HID::NpadIdType::Player2:
|
||||
case Core::HID::NpadIdType::Player3:
|
||||
case Core::HID::NpadIdType::Player4:
|
||||
case Core::HID::NpadIdType::Player5:
|
||||
case Core::HID::NpadIdType::Player6:
|
||||
case Core::HID::NpadIdType::Player7:
|
||||
case Core::HID::NpadIdType::Player8:
|
||||
case Core::HID::NpadIdType::Other:
|
||||
case Core::HID::NpadIdType::Handheld:
|
||||
return true;
|
||||
default:
|
||||
LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
|
||||
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
||||
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
|
||||
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
||||
|
||||
if (!npad_type) {
|
||||
return VibrationInvalidStyleIndex;
|
||||
}
|
||||
if (!npad_id) {
|
||||
return VibrationInvalidNpadId;
|
||||
}
|
||||
if (!device_index) {
|
||||
return VibrationDeviceIndexOutOfRange;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::VerifyValidSixAxisSensorHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
|
||||
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
|
||||
|
||||
if (!npad_id) {
|
||||
return InvalidNpadId;
|
||||
}
|
||||
if (!device_index) {
|
||||
return NpadDeviceIndexOutOfRange;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: ControllerBase{hid_core_}, service_context{service_context_} {
|
||||
static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
@@ -103,7 +52,7 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_m
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::~Controller_NPad() {
|
||||
NPad::~NPad() {
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
auto& controller = controller_data[i];
|
||||
controller.device->DeleteCallback(controller.callback_key);
|
||||
@@ -111,8 +60,7 @@ Controller_NPad::~Controller_NPad() {
|
||||
OnRelease();
|
||||
}
|
||||
|
||||
void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
|
||||
std::size_t controller_idx) {
|
||||
void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
|
||||
if (type == Core::HID::ControllerTriggerType::All) {
|
||||
ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
|
||||
ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
|
||||
@@ -150,7 +98,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
|
||||
return;
|
||||
@@ -344,12 +292,13 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
|
||||
controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
|
||||
Common::Input::PollingMode::Active);
|
||||
}
|
||||
|
||||
SignalStyleSetChangedEvent(npad_id);
|
||||
WriteEmptyEntry(controller.shared_memory);
|
||||
hid_core.SetLastActiveController(npad_id);
|
||||
}
|
||||
|
||||
void Controller_NPad::OnInit() {
|
||||
void NPad::OnInit() {
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
@@ -383,7 +332,7 @@ void Controller_NPad::OnInit() {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {
|
||||
void NPad::WriteEmptyEntry(NpadInternalState* npad) {
|
||||
NPadGenericState dummy_pad_state{};
|
||||
NpadGcTriggerState dummy_gc_state{};
|
||||
dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
|
||||
@@ -404,7 +353,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {
|
||||
npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
|
||||
}
|
||||
|
||||
void Controller_NPad::OnRelease() {
|
||||
void NPad::OnRelease() {
|
||||
is_controller_initialized = false;
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
auto& controller = controller_data[i];
|
||||
@@ -415,7 +364,7 @@ void Controller_NPad::OnRelease() {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
|
||||
void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
|
||||
std::scoped_lock lock{mutex};
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
const auto controller_type = controller.device->GetNpadStyleIndex();
|
||||
@@ -457,12 +406,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
|
||||
pad_entry.l_stick = stick_state.left;
|
||||
}
|
||||
|
||||
if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) {
|
||||
if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft ||
|
||||
controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
|
||||
pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
|
||||
pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
|
||||
}
|
||||
|
||||
if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) {
|
||||
if (controller_type == Core::HID::NpadStyleIndex::JoyconRight ||
|
||||
controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
|
||||
pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
|
||||
pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
|
||||
}
|
||||
@@ -482,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
@@ -612,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
auto& controller = controller_data[i];
|
||||
|
||||
const auto& controller_type = controller.device->GetNpadStyleIndex();
|
||||
|
||||
if (controller_type == Core::HID::NpadStyleIndex::None ||
|
||||
!controller.device->IsConnected()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* npad = controller.shared_memory;
|
||||
const auto& motion_state = controller.device->GetMotions();
|
||||
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
|
||||
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
|
||||
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
|
||||
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
|
||||
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
|
||||
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
|
||||
|
||||
// Clear previous state
|
||||
sixaxis_fullkey_state = {};
|
||||
sixaxis_handheld_state = {};
|
||||
sixaxis_dual_left_state = {};
|
||||
sixaxis_dual_right_state = {};
|
||||
sixaxis_left_lifo_state = {};
|
||||
sixaxis_right_lifo_state = {};
|
||||
|
||||
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
|
||||
controller.sixaxis_at_rest = true;
|
||||
for (std::size_t e = 0; e < motion_state.size(); ++e) {
|
||||
controller.sixaxis_at_rest =
|
||||
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
|
||||
}
|
||||
}
|
||||
|
||||
const auto set_motion_state = [&](SixAxisSensorState& state,
|
||||
const Core::HID::ControllerMotion& hid_state) {
|
||||
using namespace std::literals::chrono_literals;
|
||||
static constexpr SixAxisSensorState default_motion_state = {
|
||||
.delta_time = std::chrono::nanoseconds(5ms).count(),
|
||||
.accel = {0, 0, -1.0f},
|
||||
.orientation =
|
||||
{
|
||||
Common::Vec3f{1.0f, 0, 0},
|
||||
Common::Vec3f{0, 1.0f, 0},
|
||||
Common::Vec3f{0, 0, 1.0f},
|
||||
},
|
||||
.attribute = {1},
|
||||
};
|
||||
if (!controller.sixaxis_sensor_enabled) {
|
||||
state = default_motion_state;
|
||||
return;
|
||||
}
|
||||
if (!Settings::values.motion_enabled.GetValue()) {
|
||||
state = default_motion_state;
|
||||
return;
|
||||
}
|
||||
state.attribute.is_connected.Assign(1);
|
||||
state.delta_time = std::chrono::nanoseconds(5ms).count();
|
||||
state.accel = hid_state.accel;
|
||||
state.gyro = hid_state.gyro;
|
||||
state.rotation = hid_state.rotation;
|
||||
state.orientation = hid_state.orientation;
|
||||
};
|
||||
|
||||
switch (controller_type) {
|
||||
case Core::HID::NpadStyleIndex::None:
|
||||
ASSERT(false);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
set_motion_state(sixaxis_handheld_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
|
||||
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
using namespace std::literals::chrono_literals;
|
||||
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
|
||||
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sixaxis_fullkey_state.sampling_number =
|
||||
npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_handheld_state.sampling_number =
|
||||
npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_left_state.sampling_number =
|
||||
npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_right_state.sampling_number =
|
||||
npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_left_lifo_state.sampling_number =
|
||||
npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_right_lifo_state.sampling_number =
|
||||
npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
|
||||
if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
|
||||
// This buffer only is updated on handheld on HW
|
||||
npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
|
||||
} else {
|
||||
// Handheld doesn't update this buffer on HW
|
||||
npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
|
||||
}
|
||||
|
||||
npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
|
||||
npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
|
||||
npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
|
||||
npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
|
||||
void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
|
||||
hid_core.SetSupportedStyleTag(style_set);
|
||||
|
||||
if (is_controller_initialized) {
|
||||
@@ -750,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
|
||||
is_controller_initialized = true;
|
||||
}
|
||||
|
||||
Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
|
||||
Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const {
|
||||
if (!is_controller_initialized) {
|
||||
return {Core::HID::NpadStyleSet::None};
|
||||
}
|
||||
return hid_core.GetSupportedStyleTag();
|
||||
}
|
||||
|
||||
Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
|
||||
Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
|
||||
constexpr std::size_t max_number_npad_ids = 0xa;
|
||||
const auto length = data.size();
|
||||
ASSERT(length > 0 && (length % sizeof(u32)) == 0);
|
||||
@@ -773,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
|
||||
void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
|
||||
const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
|
||||
ASSERT(max_length <= copy_amount);
|
||||
std::memcpy(data, supported_npad_id_types.data(), copy_amount);
|
||||
}
|
||||
|
||||
std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
|
||||
std::size_t NPad::GetSupportedNpadIdTypesSize() const {
|
||||
return supported_npad_id_types.size();
|
||||
}
|
||||
|
||||
void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
|
||||
void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
|
||||
if (joy_hold_type != NpadJoyHoldType::Horizontal &&
|
||||
joy_hold_type != NpadJoyHoldType::Vertical) {
|
||||
LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
|
||||
@@ -793,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
|
||||
hold_type = joy_hold_type;
|
||||
}
|
||||
|
||||
Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
|
||||
NPad::NpadJoyHoldType NPad::GetHoldType() const {
|
||||
return hold_type;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
|
||||
void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
|
||||
if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
|
||||
ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
|
||||
return;
|
||||
@@ -806,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a
|
||||
handheld_activation_mode = activation_mode;
|
||||
}
|
||||
|
||||
Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const {
|
||||
NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
|
||||
return handheld_activation_mode;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
|
||||
void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
|
||||
communication_mode = communication_mode_;
|
||||
}
|
||||
|
||||
Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const {
|
||||
NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
|
||||
return communication_mode;
|
||||
}
|
||||
|
||||
bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
|
||||
NpadJoyDeviceType npad_device_type,
|
||||
NpadJoyAssignmentMode assignment_mode) {
|
||||
bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
|
||||
NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return false;
|
||||
@@ -889,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
|
||||
std::size_t device_index,
|
||||
const Core::HID::VibrationValue& vibration_value) {
|
||||
bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
|
||||
const Core::HID::VibrationValue& vibration_value) {
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
if (!controller.device->IsConnected()) {
|
||||
return false;
|
||||
@@ -935,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
|
||||
return controller.device->SetVibration(device_index, vibration);
|
||||
}
|
||||
|
||||
void Controller_NPad::VibrateController(
|
||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle,
|
||||
const Core::HID::VibrationValue& vibration_value) {
|
||||
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||
void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
|
||||
const Core::HID::VibrationValue& vibration_value) {
|
||||
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -982,7 +803,7 @@ void Controller_NPad::VibrateController(
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::VibrateControllers(
|
||||
void NPad::VibrateControllers(
|
||||
std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
|
||||
std::span<const Core::HID::VibrationValue> vibration_values) {
|
||||
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
|
||||
@@ -999,9 +820,9 @@ void Controller_NPad::VibrateControllers(
|
||||
}
|
||||
}
|
||||
|
||||
Core::HID::VibrationValue Controller_NPad::GetLastVibration(
|
||||
Core::HID::VibrationValue NPad::GetLastVibration(
|
||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
|
||||
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1010,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
|
||||
return controller.vibration[device_index].latest_vibration_value;
|
||||
}
|
||||
|
||||
void Controller_NPad::InitializeVibrationDevice(
|
||||
void NPad::InitializeVibrationDevice(
|
||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
|
||||
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1021,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice(
|
||||
InitializeVibrationDeviceAtIndex(npad_index, device_index);
|
||||
}
|
||||
|
||||
void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
|
||||
std::size_t device_index) {
|
||||
void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
|
||||
std::size_t device_index) {
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
if (!Settings::values.vibration_enabled.GetValue()) {
|
||||
controller.vibration[device_index].device_mounted = false;
|
||||
@@ -1033,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
|
||||
controller.device->IsVibrationEnabled(device_index);
|
||||
}
|
||||
|
||||
void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
|
||||
void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
|
||||
permit_vibration_session_enabled = permit_vibration_session;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsVibrationDeviceMounted(
|
||||
bool NPad::IsVibrationDeviceMounted(
|
||||
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
|
||||
if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
|
||||
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1048,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted(
|
||||
return controller.vibration[device_index].device_mounted;
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
|
||||
Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
// Fallback to player 1
|
||||
@@ -1060,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad
|
||||
return controller.styleset_changed_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
|
||||
void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
|
||||
const auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
controller.styleset_changed_event->Signal();
|
||||
}
|
||||
|
||||
void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
|
||||
Core::HID::NpadIdType npad_id) {
|
||||
void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) {
|
||||
UpdateControllerAt(controller, npad_id, true);
|
||||
}
|
||||
|
||||
void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
|
||||
Core::HID::NpadIdType npad_id, bool connected) {
|
||||
void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id,
|
||||
bool connected) {
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
if (!connected) {
|
||||
DisconnectNpad(npad_id);
|
||||
@@ -1082,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
|
||||
InitNewlyAddedController(npad_id);
|
||||
}
|
||||
|
||||
Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return InvalidNpadId;
|
||||
@@ -1131,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::SetGyroscopeZeroDriftMode(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode drift_mode) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
sixaxis.gyroscope_zero_drift_mode = drift_mode;
|
||||
controller.device->SetGyroscopeZeroDriftMode(drift_mode);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::GetGyroscopeZeroDriftMode(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
drift_mode = sixaxis.gyroscope_zero_drift_mode;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_at_rest) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
is_at_rest = controller.sixaxis_at_rest;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
|
||||
Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
@@ -1189,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.unaltered_passtrough = is_enabled;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
is_enabled = sixaxis.unaltered_passtrough;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
// TODO: Request this data to the controller. On error return 0xd8ca
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
calibration = sixaxis.calibration;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::GetSixAxisSensorIcInformation(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorIcInformation& ic_information) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
// TODO: Request this data to the controller. On error return 0xd8ca
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
ic_information = sixaxis.ic_information;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
@@ -1259,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool sixaxis_status) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
controller.sixaxis_sensor_enabled = sixaxis_status;
|
||||
return ResultSuccess;
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
|
||||
}
|
||||
|
||||
Result Controller_NPad::IsSixAxisSensorFusionEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
is_fusion_enabled = sixaxis.is_fusion_enabled;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
Result Controller_NPad::SetSixAxisFusionEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.is_fusion_enabled = is_fusion_enabled;
|
||||
|
||||
return ResultSuccess;
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
|
||||
}
|
||||
|
||||
Result Controller_NPad::SetSixAxisFusionParameters(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto param1 = sixaxis_fusion_parameters.parameter1;
|
||||
if (param1 < 0.0f || param1 > 1.0f) {
|
||||
return InvalidSixAxisFusionRange;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.fusion = sixaxis_fusion_parameters;
|
||||
|
||||
return ResultSuccess;
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
|
||||
}
|
||||
|
||||
Result Controller_NPad::GetSixAxisFusionParameters(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters& parameters) const {
|
||||
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
parameters = sixaxis.fusion;
|
||||
|
||||
return ResultSuccess;
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
|
||||
}
|
||||
|
||||
Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2) {
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
|
||||
}
|
||||
|
||||
NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
|
||||
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
|
||||
}
|
||||
|
||||
Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2) {
|
||||
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
|
||||
npad_id_2);
|
||||
@@ -1397,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_NPad::StartLRAssignmentMode() {
|
||||
void NPad::StartLRAssignmentMode() {
|
||||
// Nothing internally is used for lr assignment mode. Since we have the ability to set the
|
||||
// controller types from boot, it doesn't really matter about showing a selection screen
|
||||
is_in_lr_assignment_mode = true;
|
||||
}
|
||||
|
||||
void Controller_NPad::StopLRAssignmentMode() {
|
||||
void NPad::StopLRAssignmentMode() {
|
||||
is_in_lr_assignment_mode = false;
|
||||
}
|
||||
|
||||
Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
|
||||
Core::HID::NpadIdType npad_id_2) {
|
||||
Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) {
|
||||
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
|
||||
npad_id_2);
|
||||
@@ -1439,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
|
||||
Core::HID::LedPattern& pattern) const {
|
||||
Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return InvalidNpadId;
|
||||
@@ -1450,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
|
||||
bool& is_valid) const {
|
||||
Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
|
||||
bool& is_valid) const {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return InvalidNpadId;
|
||||
@@ -1461,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
|
||||
bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
|
||||
Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
|
||||
Core::HID::NpadIdType npad_id) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
return InvalidNpadId;
|
||||
@@ -1472,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
|
||||
void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
|
||||
analog_stick_use_center_clamp = use_center_clamp;
|
||||
}
|
||||
|
||||
void Controller_NPad::ClearAllConnectedControllers() {
|
||||
void NPad::ClearAllConnectedControllers() {
|
||||
for (auto& controller : controller_data) {
|
||||
if (controller.device->IsConnected() &&
|
||||
controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
|
||||
@@ -1486,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectAllConnectedControllers() {
|
||||
void NPad::DisconnectAllConnectedControllers() {
|
||||
for (auto& controller : controller_data) {
|
||||
controller.device->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectAllDisconnectedControllers() {
|
||||
void NPad::ConnectAllDisconnectedControllers() {
|
||||
for (auto& controller : controller_data) {
|
||||
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
|
||||
!controller.device->IsConnected()) {
|
||||
@@ -1501,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::ClearAllControllers() {
|
||||
void NPad::ClearAllControllers() {
|
||||
for (auto& controller : controller_data) {
|
||||
controller.device->Disconnect();
|
||||
controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
|
||||
}
|
||||
}
|
||||
|
||||
Core::HID::NpadButton Controller_NPad::GetAndResetPressState() {
|
||||
Core::HID::NpadButton NPad::GetAndResetPressState() {
|
||||
return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
|
||||
}
|
||||
|
||||
void Controller_NPad::ApplyNpadSystemCommonPolicy() {
|
||||
void NPad::ApplyNpadSystemCommonPolicy() {
|
||||
Core::HID::NpadStyleTag styletag{};
|
||||
styletag.fullkey.Assign(1);
|
||||
styletag.handheld.Assign(1);
|
||||
@@ -1537,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() {
|
||||
supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
|
||||
bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
|
||||
if (controller == Core::HID::NpadStyleIndex::Handheld) {
|
||||
const bool support_handheld =
|
||||
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
|
||||
@@ -1588,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
|
||||
return false;
|
||||
}
|
||||
|
||||
Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
|
||||
NPad::NpadControllerData& NPad::GetControllerFromHandle(
|
||||
const Core::HID::VibrationDeviceHandle& device_handle) {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
|
||||
const NPad::NpadControllerData& NPad::GetControllerFromHandle(
|
||||
const Core::HID::VibrationDeviceHandle& device_handle) const {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
|
||||
Core::HID::NpadIdType npad_id) {
|
||||
NPad::NpadControllerData& NPad::GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
const NPad::NpadControllerData& NPad::GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
npad_id = Core::HID::NpadIdType::Player1;
|
||||
}
|
||||
const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
|
||||
const auto npad_index = NpadIdTypeToIndex(npad_id);
|
||||
return controller_data[npad_index];
|
||||
}
|
||||
|
||||
const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
|
||||
const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
|
||||
Core::HID::NpadIdType npad_id) const {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
npad_id = Core::HID::NpadIdType::Player1;
|
||||
}
|
||||
const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
|
||||
const auto npad_index = NpadIdTypeToIndex(npad_id);
|
||||
return controller_data[npad_index];
|
||||
}
|
||||
|
||||
Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
|
||||
Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
@@ -1655,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
|
||||
}
|
||||
}
|
||||
|
||||
const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
@@ -1678,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.sixaxis_fullkey;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.sixaxis_handheld;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.sixaxis_dual_left;
|
||||
}
|
||||
return controller.sixaxis_dual_right;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.sixaxis_left;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.sixaxis_right;
|
||||
default:
|
||||
return controller.sixaxis_unknown;
|
||||
}
|
||||
}
|
||||
NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
|
||||
const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
|
||||
|
||||
const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.sixaxis_fullkey;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.sixaxis_handheld;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.sixaxis_dual_left;
|
||||
}
|
||||
return controller.sixaxis_dual_right;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.sixaxis_left;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.sixaxis_right;
|
||||
default:
|
||||
return controller.sixaxis_unknown;
|
||||
}
|
||||
return {
|
||||
.ui_variant = 0,
|
||||
.footer = shared_memory->applet_footer_type,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
@@ -34,11 +33,11 @@ union Result;
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
class Controller_NPad final : public ControllerBase {
|
||||
class NPad final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~Controller_NPad() override;
|
||||
explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~NPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
@@ -49,9 +48,6 @@ public:
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
// When the controller is requesting a motion update for the shared memory
|
||||
void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
// This is nn::hid::NpadJoyHoldType
|
||||
enum class NpadJoyHoldType : u64 {
|
||||
Vertical = 0,
|
||||
@@ -78,6 +74,46 @@ public:
|
||||
MaxActivationMode = 3,
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiAttributesSet
|
||||
struct AppletFooterUiAttributes {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiType
|
||||
enum class AppletFooterUiType : u8 {
|
||||
None = 0,
|
||||
HandheldNone = 1,
|
||||
HandheldJoyConLeftOnly = 2,
|
||||
HandheldJoyConRightOnly = 3,
|
||||
HandheldJoyConLeftJoyConRight = 4,
|
||||
JoyDual = 5,
|
||||
JoyDualLeftOnly = 6,
|
||||
JoyDualRightOnly = 7,
|
||||
JoyLeftHorizontal = 8,
|
||||
JoyLeftVertical = 9,
|
||||
JoyRightHorizontal = 10,
|
||||
JoyRightVertical = 11,
|
||||
SwitchProController = 12,
|
||||
CompatibleProController = 13,
|
||||
CompatibleJoyCon = 14,
|
||||
LarkHvc1 = 15,
|
||||
LarkHvc2 = 16,
|
||||
LarkNesLeft = 17,
|
||||
LarkNesRight = 18,
|
||||
Lucia = 19,
|
||||
Verification = 20,
|
||||
Lagon = 21,
|
||||
};
|
||||
|
||||
using AppletFooterUiVariant = u8;
|
||||
|
||||
// This is "nn::hid::system::AppletDetailedUiType".
|
||||
struct AppletDetailedUiType {
|
||||
AppletFooterUiVariant ui_variant;
|
||||
INSERT_PADDING_BYTES(0x2);
|
||||
AppletFooterUiType footer;
|
||||
};
|
||||
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
|
||||
// This is nn::hid::NpadCommunicationMode
|
||||
enum class NpadCommunicationMode : u64 {
|
||||
Mode_5ms = 0,
|
||||
@@ -86,6 +122,15 @@ public:
|
||||
Default = 3,
|
||||
};
|
||||
|
||||
enum class NpadRevision : u32 {
|
||||
Revision0 = 0,
|
||||
Revision1 = 1,
|
||||
Revision2 = 2,
|
||||
Revision3 = 3,
|
||||
};
|
||||
|
||||
using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
|
||||
|
||||
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
|
||||
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
|
||||
|
||||
@@ -138,37 +183,18 @@ public:
|
||||
|
||||
Result DisconnectNpad(Core::HID::NpadIdType npad_id);
|
||||
|
||||
Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode drift_mode);
|
||||
Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
|
||||
Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_at_rest) const;
|
||||
Result IsFirmwareUpdateAvailableForSixAxisSensor(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
|
||||
Result EnableSixAxisSensorUnalteredPassthrough(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
|
||||
Result IsSixAxisSensorUnalteredPassthroughEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
|
||||
Result LoadSixAxisSensorCalibrationParameter(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
|
||||
Result GetSixAxisSensorIcInformation(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorIcInformation& ic_information) const;
|
||||
Result ResetIsSixAxisSensorDeviceNewlyAssigned(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle);
|
||||
Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool sixaxis_status);
|
||||
Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_fusion_enabled) const;
|
||||
Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool is_fusion_enabled);
|
||||
Result SetSixAxisFusionParameters(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
|
||||
Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters& parameters) const;
|
||||
|
||||
SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
|
||||
SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
|
||||
|
||||
Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
|
||||
Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
|
||||
bool& is_enabled) const;
|
||||
@@ -192,10 +218,7 @@ public:
|
||||
|
||||
void ApplyNpadSystemCommonPolicy();
|
||||
|
||||
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
|
||||
static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
|
||||
static Result VerifyValidSixAxisSensorHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NPAD_COUNT = 10;
|
||||
@@ -254,29 +277,6 @@ private:
|
||||
};
|
||||
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
|
||||
|
||||
// This is nn::hid::SixAxisSensorAttribute
|
||||
struct SixAxisSensorAttribute {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> is_connected;
|
||||
BitField<1, 1, u32> is_interpolated;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
|
||||
|
||||
// This is nn::hid::SixAxisSensorState
|
||||
struct SixAxisSensorState {
|
||||
s64 delta_time{};
|
||||
s64 sampling_number{};
|
||||
Common::Vec3f accel{};
|
||||
Common::Vec3f gyro{};
|
||||
Common::Vec3f rotation{};
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
SixAxisSensorAttribute attribute{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
|
||||
|
||||
// This is nn::hid::server::NpadGcTriggerState
|
||||
struct NpadGcTriggerState {
|
||||
s64 sampling_number{};
|
||||
@@ -353,37 +353,6 @@ private:
|
||||
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
|
||||
"NfcXcdDeviceHandleStateImpl is an invalid size");
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiAttributesSet
|
||||
struct AppletFooterUiAttributes {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
|
||||
// This is nn::hid::system::AppletFooterUiType
|
||||
enum class AppletFooterUiType : u8 {
|
||||
None = 0,
|
||||
HandheldNone = 1,
|
||||
HandheldJoyConLeftOnly = 2,
|
||||
HandheldJoyConRightOnly = 3,
|
||||
HandheldJoyConLeftJoyConRight = 4,
|
||||
JoyDual = 5,
|
||||
JoyDualLeftOnly = 6,
|
||||
JoyDualRightOnly = 7,
|
||||
JoyLeftHorizontal = 8,
|
||||
JoyLeftVertical = 9,
|
||||
JoyRightHorizontal = 10,
|
||||
JoyRightVertical = 11,
|
||||
SwitchProController = 12,
|
||||
CompatibleProController = 13,
|
||||
CompatibleJoyCon = 14,
|
||||
LarkHvc1 = 15,
|
||||
LarkHvc2 = 16,
|
||||
LarkNesLeft = 17,
|
||||
LarkNesRight = 18,
|
||||
Lucia = 19,
|
||||
Verification = 20,
|
||||
Lagon = 21,
|
||||
};
|
||||
|
||||
// This is nn::hid::NpadLarkType
|
||||
enum class NpadLarkType : u32 {
|
||||
Invalid,
|
||||
@@ -427,12 +396,12 @@ private:
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
|
||||
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
|
||||
DeviceType device_type{};
|
||||
INSERT_PADDING_BYTES(0x4); // Reserved
|
||||
NPadSystemProperties system_properties{};
|
||||
@@ -466,16 +435,6 @@ private:
|
||||
std::chrono::steady_clock::time_point last_vibration_timepoint{};
|
||||
};
|
||||
|
||||
struct SixaxisParameters {
|
||||
bool is_fusion_enabled{true};
|
||||
bool unaltered_passtrough{false};
|
||||
Core::HID::SixAxisSensorFusionParameters fusion{};
|
||||
Core::HID::SixAxisSensorCalibrationParameter calibration{};
|
||||
Core::HID::SixAxisSensorIcInformation ic_information{};
|
||||
Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
|
||||
Core::HID::GyroscopeZeroDriftMode::Standard};
|
||||
};
|
||||
|
||||
struct NpadControllerData {
|
||||
Kernel::KEvent* styleset_changed_event{};
|
||||
NpadInternalState* shared_memory = nullptr;
|
||||
@@ -489,27 +448,10 @@ private:
|
||||
bool is_dual_left_connected{true};
|
||||
bool is_dual_right_connected{true};
|
||||
|
||||
// Motion parameters
|
||||
bool sixaxis_at_rest{true};
|
||||
bool sixaxis_sensor_enabled{true};
|
||||
SixaxisParameters sixaxis_fullkey{};
|
||||
SixaxisParameters sixaxis_handheld{};
|
||||
SixaxisParameters sixaxis_dual_left{};
|
||||
SixaxisParameters sixaxis_dual_right{};
|
||||
SixaxisParameters sixaxis_left{};
|
||||
SixaxisParameters sixaxis_right{};
|
||||
SixaxisParameters sixaxis_unknown{};
|
||||
|
||||
// Current pad state
|
||||
NPadGenericState npad_pad_state{};
|
||||
NPadGenericState npad_libnx_state{};
|
||||
NpadGcTriggerState npad_trigger_state{};
|
||||
SixAxisSensorState sixaxis_fullkey_state{};
|
||||
SixAxisSensorState sixaxis_handheld_state{};
|
||||
SixAxisSensorState sixaxis_dual_left_state{};
|
||||
SixAxisSensorState sixaxis_dual_right_state{};
|
||||
SixAxisSensorState sixaxis_left_lifo_state{};
|
||||
SixAxisSensorState sixaxis_right_lifo_state{};
|
||||
int callback_key{};
|
||||
};
|
||||
|
||||
@@ -519,14 +461,14 @@ private:
|
||||
void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
|
||||
void WriteEmptyEntry(NpadInternalState* npad);
|
||||
|
||||
NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::VibrationDeviceHandle& device_handle);
|
||||
const NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::VibrationDeviceHandle& device_handle) const;
|
||||
NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
|
||||
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
|
||||
|
||||
@@ -534,9 +476,6 @@ private:
|
||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const SixaxisParameters& GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
|
||||
std::atomic<u64> press_state{};
|
||||
|
||||
|
||||
@@ -12,43 +12,43 @@
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: ControllerBase{hid_core_}, service_context{service_context_} {
|
||||
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
|
||||
operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
|
||||
}
|
||||
|
||||
Controller_Palma::~Controller_Palma() {
|
||||
Palma::~Palma() {
|
||||
service_context.CloseEvent(operation_complete_event);
|
||||
};
|
||||
|
||||
void Controller_Palma::OnInit() {}
|
||||
void Palma::OnInit() {}
|
||||
|
||||
void Controller_Palma::OnRelease() {}
|
||||
void Palma::OnRelease() {}
|
||||
|
||||
void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
|
||||
PalmaConnectionHandle& handle) {
|
||||
Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
|
||||
PalmaConnectionHandle& handle) {
|
||||
active_handle.npad_id = npad_id;
|
||||
handle = active_handle;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
|
||||
Result Palma::InitializePalma(const PalmaConnectionHandle& handle) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
ActivateController();
|
||||
Activate();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
|
||||
Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent(
|
||||
const PalmaConnectionHandle& handle) const {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
|
||||
@@ -56,9 +56,9 @@ Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
|
||||
return operation_complete_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
|
||||
PalmaOperationType& operation_type,
|
||||
PalmaOperationData& data) const {
|
||||
Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
|
||||
PalmaOperationType& operation_type,
|
||||
PalmaOperationData& data) const {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -67,8 +67,7 @@ Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& hand
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
|
||||
u64 palma_activity) {
|
||||
Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -79,8 +78,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
|
||||
PalmaFrModeType fr_mode_) {
|
||||
Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -88,7 +86,7 @@ Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
|
||||
Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -99,25 +97,25 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
|
||||
Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
|
||||
Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_Palma::ReadPalmaApplicationSection() {}
|
||||
void Palma::ReadPalmaApplicationSection() {}
|
||||
|
||||
void Controller_Palma::WritePalmaApplicationSection() {}
|
||||
void Palma::WritePalmaApplicationSection() {}
|
||||
|
||||
Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
|
||||
Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -128,7 +126,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
|
||||
Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -139,10 +137,9 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle&
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_Palma::WritePalmaActivityEntry() {}
|
||||
void Palma::WritePalmaActivityEntry() {}
|
||||
|
||||
Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
|
||||
u64 unknown) {
|
||||
Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -153,8 +150,8 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
|
||||
Common::ProcessAddress t_mem, u64 size) {
|
||||
Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
|
||||
Common::ProcessAddress t_mem, u64 size) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -165,8 +162,8 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
|
||||
s32 database_id_version_) {
|
||||
Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
|
||||
s32 database_id_version_) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -178,8 +175,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
|
||||
const PalmaConnectionHandle& handle) {
|
||||
Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -191,26 +187,26 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_Palma::SuspendPalmaFeature() {}
|
||||
void Palma::SuspendPalmaFeature() {}
|
||||
|
||||
Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
|
||||
Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
return operation.result;
|
||||
}
|
||||
void Controller_Palma::ReadPalmaPlayLog() {}
|
||||
void Palma::ReadPalmaPlayLog() {}
|
||||
|
||||
void Controller_Palma::ResetPalmaPlayLog() {}
|
||||
void Palma::ResetPalmaPlayLog() {}
|
||||
|
||||
void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
|
||||
void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
|
||||
// If true controllers are able to be paired
|
||||
is_connectable = is_all_connectable;
|
||||
}
|
||||
|
||||
void Controller_Palma::SetIsPalmaPairedConnectable() {}
|
||||
void Palma::SetIsPalmaPairedConnectable() {}
|
||||
|
||||
Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
|
||||
Result Palma::PairPalma(const PalmaConnectionHandle& handle) {
|
||||
if (handle.npad_id != active_handle.npad_id) {
|
||||
return InvalidPalmaHandle;
|
||||
}
|
||||
@@ -218,14 +214,14 @@ Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {}
|
||||
void Palma::SetPalmaBoostMode(bool boost_mode) {}
|
||||
|
||||
void Controller_Palma::CancelWritePalmaWaveEntry() {}
|
||||
void Palma::CancelWritePalmaWaveEntry() {}
|
||||
|
||||
void Controller_Palma::EnablePalmaBoostMode() {}
|
||||
void Palma::EnablePalmaBoostMode() {}
|
||||
|
||||
void Controller_Palma::GetPalmaBluetoothAddress() {}
|
||||
void Palma::GetPalmaBluetoothAddress() {}
|
||||
|
||||
void Controller_Palma::SetDisallowedPalmaConnection() {}
|
||||
void Palma::SetDisallowedPalmaConnection() {}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -23,7 +23,7 @@ class EmulatedController;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Palma final : public ControllerBase {
|
||||
class Palma final : public ControllerBase {
|
||||
public:
|
||||
using PalmaOperationData = std::array<u8, 0x140>;
|
||||
|
||||
@@ -97,9 +97,9 @@ public:
|
||||
static_assert(sizeof(PalmaConnectionHandle) == 0x8,
|
||||
"PalmaConnectionHandle has incorrect size.");
|
||||
|
||||
explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~Controller_Palma() override;
|
||||
explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~Palma() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hid/emulated_console.h"
|
||||
#include "core/hid/emulated_devices.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/console_sixaxis.h"
|
||||
#include "core/hle/service/hid/controllers/seven_six_axis.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
|
||||
|
||||
Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_)
|
||||
SevenSixAxis::SevenSixAxis(Core::System& system_)
|
||||
: ControllerBase{system_.HIDCore()}, system{system_} {
|
||||
console = hid_core.GetEmulatedConsole();
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
|
||||
"ConsoleSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
}
|
||||
|
||||
Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
|
||||
SevenSixAxis::~SevenSixAxis() = default;
|
||||
|
||||
void Controller_ConsoleSixAxis::OnInit() {}
|
||||
void SevenSixAxis::OnInit() {}
|
||||
void SevenSixAxis::OnRelease() {}
|
||||
|
||||
void Controller_ConsoleSixAxis::OnRelease() {}
|
||||
|
||||
void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated() || transfer_memory == 0) {
|
||||
seven_sixaxis_lifo.buffer_count = 0;
|
||||
seven_sixaxis_lifo.buffer_tail = 0;
|
||||
@@ -53,22 +50,17 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
|
||||
-motion_status.quaternion.xyz.z,
|
||||
};
|
||||
|
||||
shared_memory->sampling_number++;
|
||||
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
shared_memory->verticalization_error = motion_status.verticalization_error;
|
||||
shared_memory->gyro_bias = motion_status.gyro_bias;
|
||||
|
||||
// Update seven six axis transfer memory
|
||||
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
|
||||
system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
|
||||
sizeof(seven_sixaxis_lifo));
|
||||
}
|
||||
|
||||
void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
|
||||
void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
|
||||
transfer_memory = t_mem;
|
||||
}
|
||||
|
||||
void Controller_ConsoleSixAxis::ResetTimestamp() {
|
||||
void SevenSixAxis::ResetTimestamp() {
|
||||
last_saved_timestamp = last_global_timestamp;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
@@ -1,10 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/quaternion.h"
|
||||
#include "common/typed_address.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
@@ -19,10 +18,10 @@ class EmulatedConsole;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_ConsoleSixAxis final : public ControllerBase {
|
||||
class SevenSixAxis final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_);
|
||||
~Controller_ConsoleSixAxis() override;
|
||||
explicit SevenSixAxis(Core::System& system_);
|
||||
~SevenSixAxis() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
@@ -51,28 +50,16 @@ private:
|
||||
};
|
||||
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
|
||||
struct ConsoleSharedMemory {
|
||||
u64 sampling_number{};
|
||||
bool is_seven_six_axis_sensor_at_rest{};
|
||||
INSERT_PADDING_BYTES(3); // padding
|
||||
f32 verticalization_error{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
};
|
||||
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
|
||||
|
||||
Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
|
||||
static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
|
||||
|
||||
SevenSixAxisState next_seven_sixaxis_state{};
|
||||
Common::ProcessAddress transfer_memory{};
|
||||
ConsoleSharedMemory* shared_memory = nullptr;
|
||||
Core::HID::EmulatedConsole* console = nullptr;
|
||||
|
||||
u64 last_saved_timestamp{};
|
||||
u64 last_global_timestamp{};
|
||||
|
||||
SevenSixAxisState next_seven_sixaxis_state{};
|
||||
Common::ProcessAddress transfer_memory{};
|
||||
Core::HID::EmulatedConsole* console = nullptr;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
413
src/core/hle/service/hid/controllers/six_axis.cpp
Normal file
413
src/core/hle/service/hid/controllers/six_axis.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/controllers/six_axis.h"
|
||||
#include "core/hle/service/hid/errors.h"
|
||||
#include "core/hle/service/hid/hid_util.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_)
|
||||
: ControllerBase{hid_core_}, npad{npad_} {
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
auto& controller = controller_data[i];
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
SixAxis::~SixAxis() = default;
|
||||
|
||||
void SixAxis::OnInit() {}
|
||||
void SixAxis::OnRelease() {}
|
||||
|
||||
void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
auto& controller = controller_data[i];
|
||||
|
||||
const auto npad_id = IndexToNpadIdType(i);
|
||||
const auto& controller_type = controller.device->GetNpadStyleIndex();
|
||||
|
||||
if (controller_type == Core::HID::NpadStyleIndex::None ||
|
||||
!controller.device->IsConnected()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& motion_state = controller.device->GetMotions();
|
||||
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
|
||||
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
|
||||
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
|
||||
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
|
||||
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
|
||||
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
|
||||
|
||||
auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id);
|
||||
auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id);
|
||||
auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id);
|
||||
auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id);
|
||||
auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id);
|
||||
auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id);
|
||||
|
||||
// Clear previous state
|
||||
sixaxis_fullkey_state = {};
|
||||
sixaxis_handheld_state = {};
|
||||
sixaxis_dual_left_state = {};
|
||||
sixaxis_dual_right_state = {};
|
||||
sixaxis_left_lifo_state = {};
|
||||
sixaxis_right_lifo_state = {};
|
||||
|
||||
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
|
||||
controller.sixaxis_at_rest = true;
|
||||
for (std::size_t e = 0; e < motion_state.size(); ++e) {
|
||||
controller.sixaxis_at_rest =
|
||||
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
|
||||
}
|
||||
}
|
||||
|
||||
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
|
||||
const Core::HID::ControllerMotion& hid_state) {
|
||||
using namespace std::literals::chrono_literals;
|
||||
static constexpr Core::HID::SixAxisSensorState default_motion_state = {
|
||||
.delta_time = std::chrono::nanoseconds(5ms).count(),
|
||||
.accel = {0, 0, -1.0f},
|
||||
.orientation =
|
||||
{
|
||||
Common::Vec3f{1.0f, 0, 0},
|
||||
Common::Vec3f{0, 1.0f, 0},
|
||||
Common::Vec3f{0, 0, 1.0f},
|
||||
},
|
||||
.attribute = {1},
|
||||
};
|
||||
if (!controller.sixaxis_sensor_enabled) {
|
||||
state = default_motion_state;
|
||||
return;
|
||||
}
|
||||
if (!Settings::values.motion_enabled.GetValue()) {
|
||||
state = default_motion_state;
|
||||
return;
|
||||
}
|
||||
state.attribute.is_connected.Assign(1);
|
||||
state.delta_time = std::chrono::nanoseconds(5ms).count();
|
||||
state.accel = hid_state.accel;
|
||||
state.gyro = hid_state.gyro;
|
||||
state.rotation = hid_state.rotation;
|
||||
state.orientation = hid_state.orientation;
|
||||
};
|
||||
|
||||
switch (controller_type) {
|
||||
case Core::HID::NpadStyleIndex::None:
|
||||
ASSERT(false);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
set_motion_state(sixaxis_handheld_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
|
||||
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
using namespace std::literals::chrono_literals;
|
||||
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
|
||||
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sixaxis_fullkey_state.sampling_number =
|
||||
sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_handheld_state.sampling_number =
|
||||
sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_left_state.sampling_number =
|
||||
sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_dual_right_state.sampling_number =
|
||||
sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_left_lifo_state.sampling_number =
|
||||
sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
sixaxis_right_lifo_state.sampling_number =
|
||||
sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
|
||||
|
||||
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
|
||||
// This buffer only is updated on handheld on HW
|
||||
sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
|
||||
} else {
|
||||
// Handheld doesn't update this buffer on HW
|
||||
sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
|
||||
}
|
||||
|
||||
sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
|
||||
sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
|
||||
sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
|
||||
sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
|
||||
}
|
||||
}
|
||||
|
||||
Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode drift_mode) {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
sixaxis.gyroscope_zero_drift_mode = drift_mode;
|
||||
controller.device->SetGyroscopeZeroDriftMode(drift_mode);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
drift_mode = sixaxis.gyroscope_zero_drift_mode;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_at_rest) const {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
is_at_rest = controller.sixaxis_at_rest;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::LoadSixAxisSensorCalibrationParameter(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
// TODO: Request this data to the controller. On error return 0xd8ca
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
calibration = sixaxis.calibration;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::GetSixAxisSensorIcInformation(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorIcInformation& ic_information) const {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
// TODO: Request this data to the controller. On error return 0xd8ca
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
ic_information = sixaxis.ic_information;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::EnableSixAxisSensorUnalteredPassthrough(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.unaltered_passtrough = is_enabled;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
is_enabled = sixaxis.unaltered_passtrough;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool sixaxis_status) {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
controller.sixaxis_sensor_enabled = sixaxis_status;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_fusion_enabled) const {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
is_fusion_enabled = sixaxis.is_fusion_enabled;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool is_fusion_enabled) {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.is_fusion_enabled = is_fusion_enabled;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::SetSixAxisFusionParameters(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto param1 = sixaxis_fusion_parameters.parameter1;
|
||||
if (param1 < 0.0f || param1 > 1.0f) {
|
||||
return InvalidSixAxisFusionRange;
|
||||
}
|
||||
|
||||
auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
sixaxis.fusion = sixaxis_fusion_parameters;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SixAxis::GetSixAxisFusionParameters(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters& parameters) const {
|
||||
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
|
||||
if (is_valid.IsError()) {
|
||||
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
const auto& sixaxis = GetSixaxisState(sixaxis_handle);
|
||||
parameters = sixaxis.fusion;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
|
||||
auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.sixaxis_fullkey;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.sixaxis_handheld;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.sixaxis_dual_left;
|
||||
}
|
||||
return controller.sixaxis_dual_right;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.sixaxis_left;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.sixaxis_right;
|
||||
default:
|
||||
return controller.sixaxis_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
|
||||
const auto& controller = GetControllerFromHandle(sixaxis_handle);
|
||||
switch (sixaxis_handle.npad_type) {
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
case Core::HID::NpadStyleIndex::Pokeball:
|
||||
return controller.sixaxis_fullkey;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
return controller.sixaxis_handheld;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
|
||||
return controller.sixaxis_dual_left;
|
||||
}
|
||||
return controller.sixaxis_dual_right;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
return controller.sixaxis_left;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
return controller.sixaxis_right;
|
||||
default:
|
||||
return controller.sixaxis_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const {
|
||||
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
|
||||
return GetControllerFromNpadIdType(npad_id);
|
||||
}
|
||||
|
||||
SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
npad_id = Core::HID::NpadIdType::Player1;
|
||||
}
|
||||
const auto npad_index = NpadIdTypeToIndex(npad_id);
|
||||
return controller_data[npad_index];
|
||||
}
|
||||
|
||||
const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(
|
||||
Core::HID::NpadIdType npad_id) const {
|
||||
if (!IsNpadIdValid(npad_id)) {
|
||||
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
|
||||
npad_id = Core::HID::NpadIdType::Player1;
|
||||
}
|
||||
const auto npad_index = NpadIdTypeToIndex(npad_id);
|
||||
return controller_data[npad_index];
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
111
src/core/hle/service/hid/controllers/six_axis.h
Normal file
111
src/core/hle/service/hid/controllers/six_axis.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedController;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class NPad;
|
||||
|
||||
class SixAxis final : public ControllerBase {
|
||||
public:
|
||||
explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_);
|
||||
~SixAxis() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
// When the controller is released
|
||||
void OnRelease() override;
|
||||
|
||||
// When the controller is requesting an update for the shared memory
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode drift_mode);
|
||||
Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
|
||||
Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_at_rest) const;
|
||||
Result EnableSixAxisSensorUnalteredPassthrough(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
|
||||
Result IsSixAxisSensorUnalteredPassthroughEnabled(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
|
||||
Result LoadSixAxisSensorCalibrationParameter(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
|
||||
Result GetSixAxisSensorIcInformation(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorIcInformation& ic_information) const;
|
||||
Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool sixaxis_status);
|
||||
Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool& is_fusion_enabled) const;
|
||||
Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
bool is_fusion_enabled);
|
||||
Result SetSixAxisFusionParameters(
|
||||
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
|
||||
Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
|
||||
Core::HID::SixAxisSensorFusionParameters& parameters) const;
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NPAD_COUNT = 10;
|
||||
|
||||
struct SixaxisParameters {
|
||||
bool is_fusion_enabled{true};
|
||||
bool unaltered_passtrough{false};
|
||||
Core::HID::SixAxisSensorFusionParameters fusion{};
|
||||
Core::HID::SixAxisSensorCalibrationParameter calibration{};
|
||||
Core::HID::SixAxisSensorIcInformation ic_information{};
|
||||
Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
|
||||
Core::HID::GyroscopeZeroDriftMode::Standard};
|
||||
};
|
||||
|
||||
struct NpadControllerData {
|
||||
Core::HID::EmulatedController* device = nullptr;
|
||||
|
||||
// Motion parameters
|
||||
bool sixaxis_at_rest{true};
|
||||
bool sixaxis_sensor_enabled{true};
|
||||
SixaxisParameters sixaxis_fullkey{};
|
||||
SixaxisParameters sixaxis_handheld{};
|
||||
SixaxisParameters sixaxis_dual_left{};
|
||||
SixaxisParameters sixaxis_dual_right{};
|
||||
SixaxisParameters sixaxis_left{};
|
||||
SixaxisParameters sixaxis_right{};
|
||||
SixaxisParameters sixaxis_unknown{};
|
||||
|
||||
// Current pad state
|
||||
Core::HID::SixAxisSensorState sixaxis_fullkey_state{};
|
||||
Core::HID::SixAxisSensorState sixaxis_handheld_state{};
|
||||
Core::HID::SixAxisSensorState sixaxis_dual_left_state{};
|
||||
Core::HID::SixAxisSensorState sixaxis_dual_right_state{};
|
||||
Core::HID::SixAxisSensorState sixaxis_left_lifo_state{};
|
||||
Core::HID::SixAxisSensorState sixaxis_right_lifo_state{};
|
||||
int callback_key{};
|
||||
};
|
||||
|
||||
SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const SixaxisParameters& GetSixaxisState(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
|
||||
NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle);
|
||||
const NpadControllerData& GetControllerFromHandle(
|
||||
const Core::HID::SixAxisSensorHandle& device_handle) const;
|
||||
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
|
||||
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
|
||||
|
||||
std::shared_ptr<NPad> npad;
|
||||
std::array<NpadControllerData, NPAD_COUNT> controller_data{};
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -15,8 +15,7 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
|
||||
|
||||
Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
|
||||
u8* raw_shared_memory_)
|
||||
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
|
||||
"TouchSharedMemory is bigger than the shared memory");
|
||||
@@ -25,13 +24,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
|
||||
console = hid_core.GetEmulatedConsole();
|
||||
}
|
||||
|
||||
Controller_Touchscreen::~Controller_Touchscreen() = default;
|
||||
TouchScreen::~TouchScreen() = default;
|
||||
|
||||
void Controller_Touchscreen::OnInit() {}
|
||||
void TouchScreen::OnInit() {}
|
||||
|
||||
void Controller_Touchscreen::OnRelease() {}
|
||||
void TouchScreen::OnRelease() {}
|
||||
|
||||
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
|
||||
@@ -14,10 +14,10 @@ class EmulatedConsole;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Touchscreen final : public ControllerBase {
|
||||
class TouchScreen final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Controller_Touchscreen() override;
|
||||
explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~TouchScreen() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
@@ -10,20 +10,19 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
|
||||
|
||||
Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
|
||||
: ControllerBase{hid_core_} {
|
||||
XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
|
||||
static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
|
||||
"XpadSharedMemory is bigger than the shared memory");
|
||||
shared_memory = std::construct_at(
|
||||
reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
|
||||
}
|
||||
Controller_XPad::~Controller_XPad() = default;
|
||||
XPad::~XPad() = default;
|
||||
|
||||
void Controller_XPad::OnInit() {}
|
||||
void XPad::OnInit() {}
|
||||
|
||||
void Controller_XPad::OnRelease() {}
|
||||
void XPad::OnRelease() {}
|
||||
|
||||
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated()) {
|
||||
shared_memory->basic_xpad_lifo.buffer_count = 0;
|
||||
shared_memory->basic_xpad_lifo.buffer_tail = 0;
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
#include "core/hle/service/hid/ring_lifo.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_XPad final : public ControllerBase {
|
||||
class XPad final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~Controller_XPad() override;
|
||||
explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
|
||||
~XPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
void OnInit() override;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,220 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
enum class HidController : std::size_t {
|
||||
DebugPad,
|
||||
Touchscreen,
|
||||
Mouse,
|
||||
Keyboard,
|
||||
XPad,
|
||||
HomeButton,
|
||||
SleepButton,
|
||||
CaptureButton,
|
||||
InputDetector,
|
||||
UniquePad,
|
||||
NPad,
|
||||
Gesture,
|
||||
ConsoleSixAxisSensor,
|
||||
DebugMouse,
|
||||
Palma,
|
||||
|
||||
MaxControllers,
|
||||
};
|
||||
|
||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||
public:
|
||||
explicit IAppletResource(Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~IAppletResource() override;
|
||||
|
||||
void ActivateController(HidController controller);
|
||||
void DeactivateController(HidController controller);
|
||||
|
||||
template <typename T>
|
||||
T& GetController(HidController controller) {
|
||||
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& GetController(HidController controller) const {
|
||||
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void MakeController(HidController controller, u8* shared_memory) {
|
||||
if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
|
||||
controllers[static_cast<std::size_t>(controller)] =
|
||||
std::make_unique<T>(system, shared_memory);
|
||||
} else {
|
||||
controllers[static_cast<std::size_t>(controller)] =
|
||||
std::make_unique<T>(system.HIDCore(), shared_memory);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
|
||||
controllers[static_cast<std::size_t>(controller)] =
|
||||
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
|
||||
}
|
||||
|
||||
void GetSharedMemoryHandle(HLERequestContext& ctx);
|
||||
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> npad_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> default_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
||||
|
||||
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
|
||||
controllers{};
|
||||
};
|
||||
|
||||
class Hid final : public ServiceFramework<Hid> {
|
||||
public:
|
||||
explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_);
|
||||
~Hid() override;
|
||||
|
||||
std::shared_ptr<IAppletResource> GetAppletResource();
|
||||
|
||||
private:
|
||||
void CreateAppletResource(HLERequestContext& ctx);
|
||||
void ActivateDebugPad(HLERequestContext& ctx);
|
||||
void ActivateTouchScreen(HLERequestContext& ctx);
|
||||
void ActivateMouse(HLERequestContext& ctx);
|
||||
void ActivateKeyboard(HLERequestContext& ctx);
|
||||
void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
|
||||
void ActivateXpad(HLERequestContext& ctx);
|
||||
void GetXpadIDs(HLERequestContext& ctx);
|
||||
void ActivateSixAxisSensor(HLERequestContext& ctx);
|
||||
void DeactivateSixAxisSensor(HLERequestContext& ctx);
|
||||
void StartSixAxisSensor(HLERequestContext& ctx);
|
||||
void StopSixAxisSensor(HLERequestContext& ctx);
|
||||
void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
|
||||
void EnableSixAxisSensorFusion(HLERequestContext& ctx);
|
||||
void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
||||
void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
||||
void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
|
||||
void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
||||
void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
||||
void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
|
||||
void IsSixAxisSensorAtRest(HLERequestContext& ctx);
|
||||
void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
|
||||
void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
|
||||
void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
|
||||
void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
|
||||
void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
|
||||
void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
|
||||
void ActivateGesture(HLERequestContext& ctx);
|
||||
void SetSupportedNpadStyleSet(HLERequestContext& ctx);
|
||||
void GetSupportedNpadStyleSet(HLERequestContext& ctx);
|
||||
void SetSupportedNpadIdType(HLERequestContext& ctx);
|
||||
void ActivateNpad(HLERequestContext& ctx);
|
||||
void DeactivateNpad(HLERequestContext& ctx);
|
||||
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
|
||||
void DisconnectNpad(HLERequestContext& ctx);
|
||||
void GetPlayerLedPattern(HLERequestContext& ctx);
|
||||
void ActivateNpadWithRevision(HLERequestContext& ctx);
|
||||
void SetNpadJoyHoldType(HLERequestContext& ctx);
|
||||
void GetNpadJoyHoldType(HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
|
||||
void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
|
||||
void StartLrAssignmentMode(HLERequestContext& ctx);
|
||||
void StopLrAssignmentMode(HLERequestContext& ctx);
|
||||
void SetNpadHandheldActivationMode(HLERequestContext& ctx);
|
||||
void GetNpadHandheldActivationMode(HLERequestContext& ctx);
|
||||
void SwapNpadAssignment(HLERequestContext& ctx);
|
||||
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
|
||||
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
|
||||
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
|
||||
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
||||
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
|
||||
void GetVibrationDeviceInfo(HLERequestContext& ctx);
|
||||
void SendVibrationValue(HLERequestContext& ctx);
|
||||
void GetActualVibrationValue(HLERequestContext& ctx);
|
||||
void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
|
||||
void PermitVibration(HLERequestContext& ctx);
|
||||
void IsVibrationPermitted(HLERequestContext& ctx);
|
||||
void SendVibrationValues(HLERequestContext& ctx);
|
||||
void SendVibrationGcErmCommand(HLERequestContext& ctx);
|
||||
void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
|
||||
void BeginPermitVibrationSession(HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(HLERequestContext& ctx);
|
||||
void IsVibrationDeviceMounted(HLERequestContext& ctx);
|
||||
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||
void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
|
||||
void StartSevenSixAxisSensor(HLERequestContext& ctx);
|
||||
void StopSevenSixAxisSensor(HLERequestContext& ctx);
|
||||
void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
|
||||
void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
|
||||
void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
|
||||
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
|
||||
void GetPalmaConnectionHandle(HLERequestContext& ctx);
|
||||
void InitializePalma(HLERequestContext& ctx);
|
||||
void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
|
||||
void GetPalmaOperationInfo(HLERequestContext& ctx);
|
||||
void PlayPalmaActivity(HLERequestContext& ctx);
|
||||
void SetPalmaFrModeType(HLERequestContext& ctx);
|
||||
void ReadPalmaStep(HLERequestContext& ctx);
|
||||
void EnablePalmaStep(HLERequestContext& ctx);
|
||||
void ResetPalmaStep(HLERequestContext& ctx);
|
||||
void ReadPalmaApplicationSection(HLERequestContext& ctx);
|
||||
void WritePalmaApplicationSection(HLERequestContext& ctx);
|
||||
void ReadPalmaUniqueCode(HLERequestContext& ctx);
|
||||
void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
|
||||
void WritePalmaActivityEntry(HLERequestContext& ctx);
|
||||
void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
|
||||
void WritePalmaWaveEntry(HLERequestContext& ctx);
|
||||
void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
|
||||
void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
|
||||
void SuspendPalmaFeature(HLERequestContext& ctx);
|
||||
void GetPalmaOperationResult(HLERequestContext& ctx);
|
||||
void ReadPalmaPlayLog(HLERequestContext& ctx);
|
||||
void ResetPalmaPlayLog(HLERequestContext& ctx);
|
||||
void SetIsPalmaAllConnectable(HLERequestContext& ctx);
|
||||
void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
|
||||
void PairPalma(HLERequestContext& ctx);
|
||||
void SetPalmaBoostMode(HLERequestContext& ctx);
|
||||
void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
|
||||
void EnablePalmaBoostMode(HLERequestContext& ctx);
|
||||
void GetPalmaBluetoothAddress(HLERequestContext& ctx);
|
||||
void SetDisallowedPalmaConnection(HLERequestContext& ctx);
|
||||
void SetNpadCommunicationMode(HLERequestContext& ctx);
|
||||
void GetNpadCommunicationMode(HLERequestContext& ctx);
|
||||
void SetTouchScreenConfiguration(HLERequestContext& ctx);
|
||||
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
159
src/core/hle/service/hid/hid_debug_server.cpp
Normal file
159
src/core/hle/service/hid/hid_debug_server.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/hid/hid_debug_server.h"
|
||||
#include "core/hle/service/hid/resource_manager.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
|
||||
: ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "DeactivateDebugPad"},
|
||||
{1, nullptr, "SetDebugPadAutoPilotState"},
|
||||
{2, nullptr, "UnsetDebugPadAutoPilotState"},
|
||||
{10, nullptr, "DeactivateTouchScreen"},
|
||||
{11, nullptr, "SetTouchScreenAutoPilotState"},
|
||||
{12, nullptr, "UnsetTouchScreenAutoPilotState"},
|
||||
{13, nullptr, "GetTouchScreenConfiguration"},
|
||||
{14, nullptr, "ProcessTouchScreenAutoTune"},
|
||||
{15, nullptr, "ForceStopTouchScreenManagement"},
|
||||
{16, nullptr, "ForceRestartTouchScreenManagement"},
|
||||
{17, nullptr, "IsTouchScreenManaged"},
|
||||
{20, nullptr, "DeactivateMouse"},
|
||||
{21, nullptr, "SetMouseAutoPilotState"},
|
||||
{22, nullptr, "UnsetMouseAutoPilotState"},
|
||||
{25, nullptr, "SetDebugMouseAutoPilotState"},
|
||||
{26, nullptr, "UnsetDebugMouseAutoPilotState"},
|
||||
{30, nullptr, "DeactivateKeyboard"},
|
||||
{31, nullptr, "SetKeyboardAutoPilotState"},
|
||||
{32, nullptr, "UnsetKeyboardAutoPilotState"},
|
||||
{50, nullptr, "DeactivateXpad"},
|
||||
{51, nullptr, "SetXpadAutoPilotState"},
|
||||
{52, nullptr, "UnsetXpadAutoPilotState"},
|
||||
{53, nullptr, "DeactivateJoyXpad"},
|
||||
{60, nullptr, "ClearNpadSystemCommonPolicy"},
|
||||
{61, nullptr, "DeactivateNpad"},
|
||||
{62, nullptr, "ForceDisconnectNpad"},
|
||||
{91, nullptr, "DeactivateGesture"},
|
||||
{110, nullptr, "DeactivateHomeButton"},
|
||||
{111, nullptr, "SetHomeButtonAutoPilotState"},
|
||||
{112, nullptr, "UnsetHomeButtonAutoPilotState"},
|
||||
{120, nullptr, "DeactivateSleepButton"},
|
||||
{121, nullptr, "SetSleepButtonAutoPilotState"},
|
||||
{122, nullptr, "UnsetSleepButtonAutoPilotState"},
|
||||
{123, nullptr, "DeactivateInputDetector"},
|
||||
{130, nullptr, "DeactivateCaptureButton"},
|
||||
{131, nullptr, "SetCaptureButtonAutoPilotState"},
|
||||
{132, nullptr, "UnsetCaptureButtonAutoPilotState"},
|
||||
{133, nullptr, "SetShiftAccelerometerCalibrationValue"},
|
||||
{134, nullptr, "GetShiftAccelerometerCalibrationValue"},
|
||||
{135, nullptr, "SetShiftGyroscopeCalibrationValue"},
|
||||
{136, nullptr, "GetShiftGyroscopeCalibrationValue"},
|
||||
{140, nullptr, "DeactivateConsoleSixAxisSensor"},
|
||||
{141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
|
||||
{142, nullptr, "DeactivateSevenSixAxisSensor"},
|
||||
{143, nullptr, "GetConsoleSixAxisSensorCountStates"},
|
||||
{144, nullptr, "GetAccelerometerFsr"},
|
||||
{145, nullptr, "SetAccelerometerFsr"},
|
||||
{146, nullptr, "GetAccelerometerOdr"},
|
||||
{147, nullptr, "SetAccelerometerOdr"},
|
||||
{148, nullptr, "GetGyroscopeFsr"},
|
||||
{149, nullptr, "SetGyroscopeFsr"},
|
||||
{150, nullptr, "GetGyroscopeOdr"},
|
||||
{151, nullptr, "SetGyroscopeOdr"},
|
||||
{152, nullptr, "GetWhoAmI"},
|
||||
{201, nullptr, "ActivateFirmwareUpdate"},
|
||||
{202, nullptr, "DeactivateFirmwareUpdate"},
|
||||
{203, nullptr, "StartFirmwareUpdate"},
|
||||
{204, nullptr, "GetFirmwareUpdateStage"},
|
||||
{205, nullptr, "GetFirmwareVersion"},
|
||||
{206, nullptr, "GetDestinationFirmwareVersion"},
|
||||
{207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
|
||||
{208, nullptr, "StartFirmwareUpdateForRevert"},
|
||||
{209, nullptr, "GetAvailableFirmwareVersionForRevert"},
|
||||
{210, nullptr, "IsFirmwareUpdatingDevice"},
|
||||
{211, nullptr, "StartFirmwareUpdateIndividual"},
|
||||
{215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
|
||||
{216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
|
||||
{221, nullptr, "UpdateControllerColor"},
|
||||
{222, nullptr, "ConnectUsbPadsAsync"},
|
||||
{223, nullptr, "DisconnectUsbPadsAsync"},
|
||||
{224, nullptr, "UpdateDesignInfo"},
|
||||
{225, nullptr, "GetUniquePadDriverState"},
|
||||
{226, nullptr, "GetSixAxisSensorDriverStates"},
|
||||
{227, nullptr, "GetRxPacketHistory"},
|
||||
{228, nullptr, "AcquireOperationEventHandle"},
|
||||
{229, nullptr, "ReadSerialFlash"},
|
||||
{230, nullptr, "WriteSerialFlash"},
|
||||
{231, nullptr, "GetOperationResult"},
|
||||
{232, nullptr, "EnableShipmentMode"},
|
||||
{233, nullptr, "ClearPairingInfo"},
|
||||
{234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
|
||||
{235, nullptr, "EnableAnalogStickPower"},
|
||||
{236, nullptr, "RequestKuinaUartClockCal"},
|
||||
{237, nullptr, "GetKuinaUartClockCal"},
|
||||
{238, nullptr, "SetKuinaUartClockTrim"},
|
||||
{239, nullptr, "KuinaLoopbackTest"},
|
||||
{240, nullptr, "RequestBatteryVoltage"},
|
||||
{241, nullptr, "GetBatteryVoltage"},
|
||||
{242, nullptr, "GetUniquePadPowerInfo"},
|
||||
{243, nullptr, "RebootUniquePad"},
|
||||
{244, nullptr, "RequestKuinaFirmwareVersion"},
|
||||
{245, nullptr, "GetKuinaFirmwareVersion"},
|
||||
{246, nullptr, "GetVidPid"},
|
||||
{247, nullptr, "GetAnalogStickCalibrationValue"},
|
||||
{248, nullptr, "GetUniquePadIdsFull"},
|
||||
{249, nullptr, "ConnectUniquePad"},
|
||||
{250, nullptr, "IsVirtual"},
|
||||
{251, nullptr, "GetAnalogStickModuleParam"},
|
||||
{301, nullptr, "GetAbstractedPadHandles"},
|
||||
{302, nullptr, "GetAbstractedPadState"},
|
||||
{303, nullptr, "GetAbstractedPadsState"},
|
||||
{321, nullptr, "SetAutoPilotVirtualPadState"},
|
||||
{322, nullptr, "UnsetAutoPilotVirtualPadState"},
|
||||
{323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
|
||||
{324, nullptr, "AttachHdlsWorkBuffer"},
|
||||
{325, nullptr, "ReleaseHdlsWorkBuffer"},
|
||||
{326, nullptr, "DumpHdlsNpadAssignmentState"},
|
||||
{327, nullptr, "DumpHdlsStates"},
|
||||
{328, nullptr, "ApplyHdlsNpadAssignmentState"},
|
||||
{329, nullptr, "ApplyHdlsStateList"},
|
||||
{330, nullptr, "AttachHdlsVirtualDevice"},
|
||||
{331, nullptr, "DetachHdlsVirtualDevice"},
|
||||
{332, nullptr, "SetHdlsState"},
|
||||
{350, nullptr, "AddRegisteredDevice"},
|
||||
{400, nullptr, "DisableExternalMcuOnNxDevice"},
|
||||
{401, nullptr, "DisableRailDeviceFiltering"},
|
||||
{402, nullptr, "EnableWiredPairing"},
|
||||
{403, nullptr, "EnableShipmentModeAutoClear"},
|
||||
{404, nullptr, "SetRailEnabled"},
|
||||
{500, nullptr, "SetFactoryInt"},
|
||||
{501, nullptr, "IsFactoryBootEnabled"},
|
||||
{550, nullptr, "SetAnalogStickModelDataTemporarily"},
|
||||
{551, nullptr, "GetAnalogStickModelData"},
|
||||
{552, nullptr, "ResetAnalogStickModelData"},
|
||||
{600, nullptr, "ConvertPadState"},
|
||||
{650, nullptr, "AddButtonPlayData"},
|
||||
{651, nullptr, "StartButtonPlayData"},
|
||||
{652, nullptr, "StopButtonPlayData"},
|
||||
{2000, nullptr, "DeactivateDigitizer"},
|
||||
{2001, nullptr, "SetDigitizerAutoPilotState"},
|
||||
{2002, nullptr, "UnsetDigitizerAutoPilotState"},
|
||||
{2002, nullptr, "ReloadFirmwareDebugSettings"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IHidDebugServer::~IHidDebugServer() = default;
|
||||
|
||||
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
|
||||
resource_manager->Initialize();
|
||||
return resource_manager;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
26
src/core/hle/service/hid/hid_debug_server.h
Normal file
26
src/core/hle/service/hid/hid_debug_server.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
class ResourceManager;
|
||||
|
||||
class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
|
||||
public:
|
||||
explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
|
||||
~IHidDebugServer() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ResourceManager> GetResourceManager();
|
||||
|
||||
std::shared_ptr<ResourceManager> resource_manager;
|
||||
};
|
||||
|
||||
} // namespace Service::HID
|
||||
99
src/core/hle/service/hid/hid_firmware_settings.cpp
Normal file
99
src/core/hle/service/hid/hid_firmware_settings.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/hid/hid_firmware_settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
HidFirmwareSettings::HidFirmwareSettings() {
|
||||
LoadSettings(true);
|
||||
}
|
||||
|
||||
void HidFirmwareSettings::Reload() {
|
||||
LoadSettings(true);
|
||||
}
|
||||
|
||||
void HidFirmwareSettings::LoadSettings(bool reload_config) {
|
||||
if (is_initalized && !reload_config) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values
|
||||
|
||||
is_debug_pad_enabled = true;
|
||||
is_device_managed = true;
|
||||
is_touch_i2c_managed = is_device_managed;
|
||||
is_future_devices_emulated = false;
|
||||
is_mcu_hardware_error_emulated = false;
|
||||
is_rail_enabled = true;
|
||||
is_firmware_update_failure_emulated = false;
|
||||
is_firmware_update_failure = {};
|
||||
is_ble_disabled = false;
|
||||
is_dscale_disabled = false;
|
||||
is_handheld_forced = true;
|
||||
features_per_id_disabled = {};
|
||||
is_touch_firmware_auto_update_disabled = false;
|
||||
is_initalized = true;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsDebugPadEnabled() {
|
||||
LoadSettings(false);
|
||||
return is_debug_pad_enabled;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsDeviceManaged() {
|
||||
LoadSettings(false);
|
||||
return is_device_managed;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsEmulateFutureDevice() {
|
||||
LoadSettings(false);
|
||||
return is_future_devices_emulated;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsTouchI2cManaged() {
|
||||
LoadSettings(false);
|
||||
return is_touch_i2c_managed;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsHandheldForced() {
|
||||
LoadSettings(false);
|
||||
return is_handheld_forced;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsRailEnabled() {
|
||||
LoadSettings(false);
|
||||
return is_rail_enabled;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsHardwareErrorEmulated() {
|
||||
LoadSettings(false);
|
||||
return is_mcu_hardware_error_emulated;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsBleDisabled() {
|
||||
LoadSettings(false);
|
||||
return is_ble_disabled;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsDscaleDisabled() {
|
||||
LoadSettings(false);
|
||||
return is_dscale_disabled;
|
||||
}
|
||||
|
||||
bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
|
||||
LoadSettings(false);
|
||||
return is_touch_firmware_auto_update_disabled;
|
||||
}
|
||||
|
||||
HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
|
||||
LoadSettings(false);
|
||||
return is_firmware_update_failure;
|
||||
}
|
||||
|
||||
HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
|
||||
LoadSettings(false);
|
||||
return features_per_id_disabled;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
54
src/core/hle/service/hid/hid_firmware_settings.h
Normal file
54
src/core/hle/service/hid/hid_firmware_settings.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
/// Loads firmware config from nn::settings::fwdbg
|
||||
class HidFirmwareSettings {
|
||||
public:
|
||||
using FirmwareSetting = std::array<u8, 4>;
|
||||
using FeaturesPerId = std::array<bool, 0xA8>;
|
||||
|
||||
HidFirmwareSettings();
|
||||
|
||||
void Reload();
|
||||
void LoadSettings(bool reload_config);
|
||||
|
||||
bool IsDebugPadEnabled();
|
||||
bool IsDeviceManaged();
|
||||
bool IsEmulateFutureDevice();
|
||||
bool IsTouchI2cManaged();
|
||||
bool IsHandheldForced();
|
||||
bool IsRailEnabled();
|
||||
bool IsHardwareErrorEmulated();
|
||||
bool IsBleDisabled();
|
||||
bool IsDscaleDisabled();
|
||||
bool IsTouchAutoUpdateDisabled();
|
||||
|
||||
FirmwareSetting GetFirmwareUpdateFailure();
|
||||
FeaturesPerId FeaturesDisabledPerId();
|
||||
|
||||
private:
|
||||
bool is_initalized{};
|
||||
|
||||
// Debug settings
|
||||
bool is_debug_pad_enabled{};
|
||||
bool is_device_managed{};
|
||||
bool is_touch_i2c_managed{};
|
||||
bool is_future_devices_emulated{};
|
||||
bool is_mcu_hardware_error_emulated{};
|
||||
bool is_rail_enabled{};
|
||||
bool is_firmware_update_failure_emulated{};
|
||||
bool is_ble_disabled{};
|
||||
bool is_dscale_disabled{};
|
||||
bool is_handheld_forced{};
|
||||
bool is_touch_firmware_auto_update_disabled{};
|
||||
FirmwareSetting is_firmware_update_failure{};
|
||||
FeaturesPerId features_per_id_disabled{};
|
||||
};
|
||||
|
||||
} // namespace Service::HID
|
||||
2371
src/core/hle/service/hid/hid_server.cpp
Normal file
2371
src/core/hle/service/hid/hid_server.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user