Compare commits

..

54 Commits

Author SHA1 Message Date
50135e480a Android 237 2024-02-09 00:56:52 +00:00
8495adfed5 Merge yuzu-emu#12964 2024-02-09 00:56:52 +00:00
0b624b8bfa Merge yuzu-emu#12955 2024-02-09 00:56:52 +00:00
98d0ccc78c Merge yuzu-emu#12951 2024-02-09 00:56:52 +00:00
bfc714e16c Merge yuzu-emu#12920 2024-02-09 00:56:51 +00:00
18fa807cd3 Merge yuzu-emu#12756 2024-02-09 00:56:51 +00:00
d5a7230036 Merge yuzu-emu#12749 2024-02-09 00:56:51 +00:00
afb77e967f Merge yuzu-emu#12461 2024-02-09 00:56:51 +00:00
71e59bdcd8 Merge pull request #12963 from t895/versioning-fix
android: Fix regex for git version
2024-02-08 17:03:32 -05:00
0a1283f94f android: Fix regex for git version 2024-02-08 14:24:15 -05:00
f049453dd6 Merge pull request #12903 from liamwhite/const-offset
shader_recompiler: use only ConstOffset for OpImageFetch
2024-02-08 17:00:45 +01:00
cac37a6f6e Merge pull request #12954 from german77/hidbus-interface
service: hid: Migrate hidbus to new interface
2024-02-08 11:00:11 -05:00
263dfa95e4 Merge pull request #12914 from FernandoS27/vc-refactor
VideoCore Refactor Part 1.
2024-02-08 10:59:59 -05:00
bc9711cb1e Merge pull request #12953 from FernandoS27/zero-fps-mah-ass
SMMU: Ensure the backing address range matches the current
2024-02-08 10:59:52 -05:00
b4d88a7bb4 service: hid: Migrate hidbus to new interface 2024-02-07 18:07:32 -06:00
ae833aa9c0 SMMU: Ensure the backing address range matches the current 2024-02-07 23:47:42 +01:00
4463ded603 Merge pull request #12939 from german77/wonder
dmnt: cheat: Invalidate cache on memory writes
2024-02-07 15:33:44 -05:00
159dec01ee Merge pull request #12932 from german77/any-key-is-good
yuzu: Make controller keys easier to assign
2024-02-07 15:33:39 -05:00
6319bafafa Merge pull request #12912 from FearlessTobi/ports-feb-24
Port some small changes from Citra (web_backend and translations)
2024-02-07 15:33:28 -05:00
c000a5ff09 Merge pull request #12909 from t895/play-store-automation
ci: android: Play store publishing setup
2024-02-07 15:32:42 -05:00
12f86f89fc yuzu: Make controller keys easier to assign 2024-02-06 16:51:39 -06:00
9858ea79fb dmnt: cheat: Invalidate cache on memory writes 2024-02-06 13:49:48 -06:00
c10e720ba9 Merge pull request #12883 from FernandoS27/memory_manager_mem
MemoryManager: Reduce the page table size based on last big page address.
2024-02-06 10:25:03 -05:00
5016de3626 Merge pull request #12928 from german77/motion-mp
service: hid: Add multiprocess support to six axis input
2024-02-06 10:24:46 -05:00
d5fb9fd12c Merge pull request #12933 from german77/irs-interface
service: irs: Migrate service to new interface
2024-02-06 10:24:30 -05:00
c79b3af610 Merge pull request #12934 from german77/hid_debug_interface
service: hid: Migrate hid debug service to new interface
2024-02-06 10:24:20 -05:00
c0a383d960 web_backend: Fix compilation 2024-02-06 15:48:04 +01:00
b6106604c4 service: hid: Migrate hid debug service to new interface 2024-02-06 00:38:46 -06:00
12b6162852 service: irs: Migrate service to new interface 2024-02-06 00:14:16 -06:00
8f192b494a service: hid: Add multiprocess support to six axis input 2024-02-05 17:19:31 -06:00
372897aac4 service: hid: Ensure aruid data is initialized 2024-02-05 17:17:21 -06:00
fa47ac1c9f Common: Rename SplitRangeSet to OverlapRangeSet 2024-02-05 23:01:17 +01:00
a2f23746c2 Merge pull request #12905 from liamwhite/hwc-release
nvnflinger: release buffers before presentation sleep
2024-02-05 13:43:22 -05:00
215b13f2a2 Merge pull request #12924 from liamwhite/pedantic-unsigned
typed_address: test values are unsigned
2024-02-05 13:43:06 -05:00
35ed9425d7 Merge pull request #12925 from german77/linux-tab
yuzu: Fully hide linux tab
2024-02-05 13:41:31 -05:00
74cc8721c7 Merge pull request #12915 from german77/cheat
dmnt: cheats: Update cheat vm to latest version
2024-02-05 13:41:21 -05:00
8ef1db78b0 Merge pull request #12916 from liamwhite/float-fix
gdb: fix load/save of fp values in a32
2024-02-05 13:41:15 -05:00
18c8f10ff2 Merge pull request #12922 from FearlessTobi/lang-mappins
.tx/config: Use language mappings for android "tx pull"
2024-02-05 13:40:53 -05:00
96d881f087 yuzu: Fully hide linux tab 2024-02-05 11:58:20 -06:00
0e950baf41 typed_address: test values are unsigned 2024-02-05 12:47:10 -05:00
8113f55f4b dmnt: cheats: Silence memory errors 2024-02-05 11:08:24 -06:00
f296a9ce9a shader_recompiler: use only ConstOffset for OpImageFetch 2024-02-05 12:01:09 -05:00
ddbefc71cb .tx/config: Use language mappings for android "tx pull"
The language names we are using in the android resources differ from those on Transifex.

We need to manually specify mappings for them, so Transifex is able to place the files in the correct folders.
2024-02-05 15:57:13 +01:00
0d5a3abeae Buffer Cache: Refactor to use Range sets instead 2024-02-05 11:06:52 +01:00
85143e8376 gdb: fix load/save of fp values in a32 2024-02-04 20:28:43 -05:00
504abbd6e0 dmnt: cheats: Update cheat vm to latest version 2024-02-04 17:46:20 -06:00
accccc0cbf NVDRV: Refactor HeapMapper to use RangeSets 2024-02-04 20:01:50 +01:00
01ba6cf610 Common: Introduce Range Sets 2024-02-04 20:01:50 +01:00
4841dc0b74 VideoCore: Move Slot Vector to Common 2024-02-04 20:01:47 +01:00
185125e4e4 citra_qt/configure_ui: Show country of language in the combobox
This prevents an issue where we had seperate versions of the same language for different regions and they were not distinguishable (e.g. "Chinese (China)" and "Chinese (Taiwan)").

Also makes it so we do not need to hardcode specific languages anymore.
2024-02-04 17:06:44 +01:00
99ea31faa8 ci: android: Play store publishing setup 2024-02-04 10:54:18 -05:00
9ade941de1 web_backend: Sync with Citra implementation
While porting https://github.com/citra-emu/citra/pull/7347, I noticed the code of yuzu was not up-to-date with the implementation from Citra.
2024-02-04 16:51:52 +01:00
5eb5c96750 nvnflinger: release buffers before presentation sleep 2024-02-03 17:14:43 -05:00
f740d8b9be MemoryManager: Reduce the page table size based on last big page address. 2024-02-01 13:00:36 +01:00
70 changed files with 1205 additions and 1521 deletions

View File

@ -0,0 +1,21 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE="$(which ccache)"
ccache -s
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${EA_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
base64 --decode <<< "${EA_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
./gradlew "publishEaReleaseBundle"
ccache -s
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi

View File

@ -0,0 +1,21 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE="$(which ccache)"
ccache -s
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${MAINLINE_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
base64 --decode <<< "${MAINLINE_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
./gradlew "publishMainlineReleaseBundle"
ccache -s
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi

View File

@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-ea-play-release
on:
workflow_dispatch:
inputs:
release-track:
description: 'Play store release track (internal/alpha/beta/production)'
required: true
default: 'alpha'
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.repository == 'yuzu-emu/yuzu' }}
steps:
- uses: actions/checkout@v3
name: Checkout
with:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- run: npm install execa@5
- uses: actions/github-script@v5
name: 'Merge and publish Android EA changes'
env:
ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }}
BUILD_EA: true
with:
script: |
const execa = require("execa");
const mergebot = require('./.github/workflows/android-merge.js').mergebot;
process.chdir('${{ github.workspace }}');
mergebot(github, context, execa);
- name: Get tag name
run: echo "GIT_TAG_NAME=$(cat tag-name.txt)" >> $GITHUB_ENV
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
- name: Build
run: ./.ci/scripts/android/eabuild.sh
env:
EA_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
EA_SERVICE_ACCOUNT_KEY_B64: ${{ secrets.EA_SERVICE_ACCOUNT_KEY_B64 }}
STORE_TRACK: ${{ github.event.inputs.release-track }}
AUTO_VERSIONED: true
BUILD_EA: true
- name: Create release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.EA_TAG_NAME }}
name: ${{ env.EA_TAG_NAME }}
draft: false
prerelease: false
repository: yuzu/yuzu-android
token: ${{ secrets.ALT_GITHUB_TOKEN }}

View File

@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-mainline-play-release
on:
workflow_dispatch:
inputs:
release-tag:
description: 'Tag # from yuzu-android that you want to build and publish'
required: true
default: '200'
release-track:
description: 'Play store release track (internal/alpha/beta/production)'
required: true
default: 'alpha'
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.repository == 'yuzu-emu/yuzu' }}
steps:
- uses: actions/checkout@v3
name: Checkout
with:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- run: npm install execa@5
- uses: actions/github-script@v5
name: 'Pull mainline tag'
env:
MAINLINE_TAG: ${{ github.event.inputs.release-tag }}
with:
script: |
const execa = require("execa");
const mergebot = require('./.github/workflows/android-merge.js').getMainlineTag;
process.chdir('${{ github.workspace }}');
mergebot(execa);
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
- name: Build
run: |
echo "GIT_TAG_NAME=android-${{ github.event.inputs.releast-tag }}" >> $GITHUB_ENV
./.ci/scripts/android/mainlinebuild.sh
env:
MAINLINE_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
SERVICE_ACCOUNT_KEY_B64: ${{ secrets.MAINLINE_SERVICE_ACCOUNT_KEY_B64 }}
STORE_TRACK: ${{ github.event.inputs.release-track }}
AUTO_VERSIONED: true

View File

@ -6,9 +6,12 @@
const fs = require("fs"); const fs = require("fs");
// which label to check for changes // which label to check for changes
const CHANGE_LABEL = 'android-merge'; const CHANGE_LABEL_MAINLINE = 'android-merge';
const CHANGE_LABEL_EA = 'android-ea-merge';
// how far back in time should we consider the changes are "recent"? (default: 24 hours) // how far back in time should we consider the changes are "recent"? (default: 24 hours)
const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000); const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000);
const BUILD_EA = process.env.BUILD_EA == 'true';
const MAINLINE_TAG = process.env.MAINLINE_TAG;
async function checkBaseChanges(github) { async function checkBaseChanges(github) {
// query the commit date of the latest commit on this branch // query the commit date of the latest commit on this branch
@ -40,20 +43,7 @@ async function checkBaseChanges(github) {
async function checkAndroidChanges(github) { async function checkAndroidChanges(github) {
if (checkBaseChanges(github)) return true; if (checkBaseChanges(github)) return true;
const query = `query($owner:String!, $name:String!, $label:String!) { const pulls = getPulls(github, false);
repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) {
nodes { number headRepository { pushedAt } }
}
}
}`;
const variables = {
owner: 'yuzu-emu',
name: 'yuzu',
label: CHANGE_LABEL,
};
const result = await github.graphql(query, variables);
const pulls = result.repository.pullRequests.nodes;
for (let i = 0; i < pulls.length; i++) { for (let i = 0; i < pulls.length; i++) {
let pull = pulls[i]; let pull = pulls[i];
if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) { if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) {
@ -83,7 +73,13 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
}; };
const tags = await github.graphql(query, variables); const tags = await github.graphql(query, variables);
const tagList = tags.repository.refs.nodes; const tagList = tags.repository.refs.nodes;
const lastTag = tagList[0] ? tagList[0].name : 'dummy-0'; let lastTag = 'android-1';
for (let i = 0; i < tagList.length; ++i) {
if (tagList[i].name.includes('android-')) {
lastTag = tagList[i].name;
break;
}
}
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0; const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
const channel = repo.split('-')[1]; const channel = repo.split('-')[1];
const newTag = `${channel}-${tagNumber + 1}`; const newTag = `${channel}-${tagNumber + 1}`;
@ -101,6 +97,48 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
console.info('Successfully pushed new changes.'); console.info('Successfully pushed new changes.');
} }
async function tagAndPushEA(github, owner, repo, execa) {
let altToken = process.env.ALT_GITHUB_TOKEN;
if (!altToken) {
throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`;
}
const query = `query ($owner:String!, $name:String!) {
repository(name:$name, owner:$owner) {
refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) {
nodes { name }
}
}
}`;
const variables = {
owner: owner,
name: repo,
};
const tags = await github.graphql(query, variables);
const tagList = tags.repository.refs.nodes;
let lastTag = 'ea-1';
for (let i = 0; i < tagList.length; ++i) {
if (tagList[i].name.includes('ea-')) {
lastTag = tagList[i].name;
break;
}
}
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
const newTag = `ea-${tagNumber + 1}`;
console.log(`New tag: ${newTag}`);
console.info('Pushing tags to GitHub ...');
await execa("git", ["remote", "add", "android", "https://github.com/yuzu-emu/yuzu-android.git"]);
await execa("git", ["fetch", "android"]);
await execa("git", ['tag', newTag]);
await execa("git", ['push', 'android', `${newTag}`]);
fs.writeFile('tag-name.txt', newTag, (err) => {
if (err) throw 'Could not write tag name to file!'
})
console.info('Successfully pushed new changes.');
}
async function generateReadme(pulls, context, mergeResults, execa) { async function generateReadme(pulls, context, mergeResults, execa) {
let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`; let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`;
let output = let output =
@ -202,10 +240,7 @@ async function resetBranch(execa) {
} }
} }
async function mergebot(github, context, execa) { async function getPulls(github) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
const query = `query ($owner:String!, $name:String!, $label:String!) { const query = `query ($owner:String!, $name:String!, $label:String!) {
repository(name:$name, owner:$owner) { repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) { pullRequests(labels: [$label], states: OPEN, first: 100) {
@ -215,13 +250,49 @@ async function mergebot(github, context, execa) {
} }
} }
}`; }`;
const variables = { const mainlineVariables = {
owner: 'yuzu-emu', owner: 'yuzu-emu',
name: 'yuzu', name: 'yuzu',
label: CHANGE_LABEL, label: CHANGE_LABEL_MAINLINE,
}; };
const result = await github.graphql(query, variables); const mainlineResult = await github.graphql(query, mainlineVariables);
const pulls = result.repository.pullRequests.nodes; const pulls = mainlineResult.repository.pullRequests.nodes;
if (BUILD_EA) {
const eaVariables = {
owner: 'yuzu-emu',
name: 'yuzu',
label: CHANGE_LABEL_EA,
};
const eaResult = await github.graphql(query, eaVariables);
const eaPulls = eaResult.repository.pullRequests.nodes;
return pulls.concat(eaPulls);
}
return pulls;
}
async function getMainlineTag(execa) {
console.log(`::group::Getting mainline tag android-${MAINLINE_TAG}`);
let hasFailed = false;
try {
await execa("git", ["remote", "add", "mainline", "https://github.com/yuzu-emu/yuzu-android.git"]);
await execa("git", ["fetch", "mainline", "--tags"]);
await execa("git", ["checkout", `tags/android-${MAINLINE_TAG}`]);
await execa("git", ["submodule", "update", "--init", "--recursive"]);
} catch (err) {
console.log('::error title=Failed pull tag');
hasFailed = true;
}
console.log("::endgroup::");
if (hasFailed) {
throw 'Failed pull mainline tag. Aborting!';
}
}
async function mergebot(github, context, execa) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
const pulls = await getPulls(github);
let displayList = []; let displayList = [];
for (let i = 0; i < pulls.length; i++) { for (let i = 0; i < pulls.length; i++) {
let pull = pulls[i]; let pull = pulls[i];
@ -231,11 +302,17 @@ async function mergebot(github, context, execa) {
console.table(displayList); console.table(displayList);
await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa); await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
const mergeResults = await mergePullRequests(pulls, execa); const mergeResults = await mergePullRequests(pulls, execa);
await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true); if (BUILD_EA) {
await tagAndPushEA(github, 'yuzu-emu', `yuzu-android`, execa);
} else {
await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true);
}
} }
module.exports.mergebot = mergebot; module.exports.mergebot = mergebot;
module.exports.checkAndroidChanges = checkAndroidChanges; module.exports.checkAndroidChanges = checkAndroidChanges;
module.exports.tagAndPush = tagAndPush; module.exports.tagAndPush = tagAndPush;
module.exports.checkBaseChanges = checkBaseChanges; module.exports.checkBaseChanges = checkBaseChanges;
module.exports.getMainlineTag = getMainlineTag;

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project # SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-publish name: yuzu-android-publish
@ -16,7 +16,7 @@ on:
jobs: jobs:
android: android:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu-android' }} if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }}
steps: steps:
# this checkout is required to make sure the GitHub Actions scripts are available # this checkout is required to make sure the GitHub Actions scripts are available
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -1,15 +1,12 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [12461](https://github.com/yuzu-emu/yuzu-android//pull/12461) | [`4c08a0e6d`](https://github.com/yuzu-emu/yuzu-android//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes | | [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`54eb8dcc9`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes | | [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12756](https://github.com/yuzu-emu/yuzu-android//pull/12756) | [`cd3de0848`](https://github.com/yuzu-emu/yuzu-android//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes | | [12756](https://github.com/yuzu-emu/yuzu//pull/12756) | [`cd3de0848`](https://github.com/yuzu-emu/yuzu//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12873](https://github.com/yuzu-emu/yuzu-android//pull/12873) | [`023c3aa65`](https://github.com/yuzu-emu/yuzu-android//pull/12873/files) | GPU: Implement channel scheduling. | [FernandoS27](https://github.com/FernandoS27/) | Yes | | [12920](https://github.com/yuzu-emu/yuzu//pull/12920) | [`2600ac65c`](https://github.com/yuzu-emu/yuzu//pull/12920/files) | android: Move JNI setup and helpers to common | [t895](https://github.com/t895/) | Yes |
| [12903](https://github.com/yuzu-emu/yuzu-android//pull/12903) | [`f296a9ce9`](https://github.com/yuzu-emu/yuzu-android//pull/12903/files) | shader_recompiler: use only ConstOffset for OpImageFetch | [liamwhite](https://github.com/liamwhite/) | Yes | | [12951](https://github.com/yuzu-emu/yuzu//pull/12951) | [`fee263c59`](https://github.com/yuzu-emu/yuzu//pull/12951/files) | ipc: additional fixes | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12905](https://github.com/yuzu-emu/yuzu-android//pull/12905) | [`5eb5c9675`](https://github.com/yuzu-emu/yuzu-android//pull/12905/files) | nvnflinger: release buffers before presentation sleep | [liamwhite](https://github.com/liamwhite/) | Yes | | [12955](https://github.com/yuzu-emu/yuzu//pull/12955) | [`8d2ad3d8f`](https://github.com/yuzu-emu/yuzu//pull/12955/files) | dmnt: cheat: Avoid invalidating cache on 32bit | [german77](https://github.com/german77/) | Yes |
| [12914](https://github.com/yuzu-emu/yuzu-android//pull/12914) | [`3a6d8ae2c`](https://github.com/yuzu-emu/yuzu-android//pull/12914/files) | VideoCore Refactor Part 1. | [FernandoS27](https://github.com/FernandoS27/) | Yes | | [12964](https://github.com/yuzu-emu/yuzu//pull/12964) | [`d93459db6`](https://github.com/yuzu-emu/yuzu//pull/12964/files) | android: Remove foreground service | [t895](https://github.com/t895/) | Yes |
| [12915](https://github.com/yuzu-emu/yuzu-android//pull/12915) | [`8113f55f4`](https://github.com/yuzu-emu/yuzu-android//pull/12915/files) | dmnt: cheats: Update cheat vm to latest version | [german77](https://github.com/german77/) | Yes |
| [12920](https://github.com/yuzu-emu/yuzu-android//pull/12920) | [`62fc6d5c3`](https://github.com/yuzu-emu/yuzu-android//pull/12920/files) | android: Move JNI setup and helpers to common | [t895](https://github.com/t895/) | Yes |
| [12924](https://github.com/yuzu-emu/yuzu-android//pull/12924) | [`0e950baf4`](https://github.com/yuzu-emu/yuzu-android//pull/12924/files) | typed_address: test values are unsigned | [liamwhite](https://github.com/liamwhite/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -11,3 +11,4 @@ type = QT
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID type = ANDROID
lang_map = ja_JP:ja, ko_KR:ko, pt_BR:pt-rBR, pt_PT:pt-rPT, ru_RU:ru, vi_VN:vi, zh_CN:zh-rCN, zh_TW:zh-rTW

View File

@ -3,8 +3,8 @@
import android.annotation.SuppressLint import android.annotation.SuppressLint
import kotlin.collections.setOf import kotlin.collections.setOf
import org.jetbrains.kotlin.konan.properties.Properties
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
plugins { plugins {
id("com.android.application") id("com.android.application")
@ -13,6 +13,7 @@ plugins {
kotlin("plugin.serialization") version "1.9.20" kotlin("plugin.serialization") version "1.9.20"
id("androidx.navigation.safeargs.kotlin") id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0" id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
id("com.github.triplet.play") version "3.8.6"
} }
/** /**
@ -58,15 +59,7 @@ android {
targetSdk = 34 targetSdk = 34
versionName = getGitVersion() versionName = getGitVersion()
// If you want to use autoVersion for the versionCode, create a property in local.properties versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
// named "autoVersioned" and set it to "true"
val properties = Properties()
val versionProperty = try {
properties.load(project.rootProject.file("local.properties").inputStream())
properties.getProperty("autoVersioned") ?: ""
} catch (e: Exception) { "" }
versionCode = if (versionProperty == "true") {
autoVersion autoVersion
} else { } else {
1 1
@ -221,6 +214,15 @@ ktlint {
} }
} }
play {
val keyPath = System.getenv("SERVICE_ACCOUNT_KEY_PATH")
if (keyPath != null) {
serviceAccountCredentials.set(File(keyPath))
}
track.set(System.getenv("STORE_TRACK") ?: "internal")
releaseStatus.set(ReleaseStatus.COMPLETED)
}
dependencies { dependencies {
implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.appcompat:appcompat:1.6.1")
@ -257,12 +259,18 @@ fun runGitCommand(command: List<String>): String {
} }
fun getGitVersion(): String { fun getGitVersion(): String {
val gitVersion = runGitCommand(
listOf(
"git",
"describe",
"--always",
"--long"
)
).replace(Regex("(-0)?-[^-]+$"), "")
val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME") ?: "" System.getenv("GIT_TAG_NAME") ?: gitVersion
gitTag
} else { } else {
runGitCommand(listOf("git", "describe", "--always", "--long")) gitVersion
.replace(Regex("(-0)?-[^-]+$"), "")
} }
return versionName.ifEmpty { "0.0" } return versionName.ifEmpty { "0.0" }
} }

View File

@ -12,8 +12,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" /> <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
@ -80,10 +78,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:resource="@xml/nfc_tech_filter" /> android:resource="@xml/nfc_tech_filter" />
</activity> </activity>
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
</service>
<provider <provider
android:name=".features.DocumentProvider" android:name=".features.DocumentProvider"
android:authorities="${applicationId}.user" android:authorities="${applicationId}.user"

View File

@ -17,17 +17,6 @@ fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() { class YuzuApplication : Application() {
private fun createNotificationChannels() { private fun createNotificationChannels() {
val emulationChannel = NotificationChannel(
getString(R.string.emulation_notification_channel_id),
getString(R.string.emulation_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
)
emulationChannel.description = getString(
R.string.emulation_notification_channel_description
)
emulationChannel.setSound(null, null)
emulationChannel.vibrationPattern = null
val noticeChannel = NotificationChannel( val noticeChannel = NotificationChannel(
getString(R.string.notice_notification_channel_id), getString(R.string.notice_notification_channel_id),
getString(R.string.notice_notification_channel_name), getString(R.string.notice_notification_channel_name),
@ -39,7 +28,6 @@ class YuzuApplication : Application() {
// Register the channel with the system; you can't change the importance // Register the channel with the system; you can't change the importance
// or other notification behaviors after this // or other notification behaviors after this
val notificationManager = getSystemService(NotificationManager::class.java) val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(emulationChannel)
notificationManager.createNotificationChannel(noticeChannel) notificationManager.createNotificationChannel(noticeChannel)
} }

View File

@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.activities package org.yuzu.yuzu_emu.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent import android.app.PendingIntent
import android.app.PictureInPictureParams import android.app.PictureInPictureParams
import android.app.RemoteAction import android.app.RemoteAction
@ -45,7 +44,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.MemoryUtil
@ -74,11 +72,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private val emulationViewModel: EmulationViewModel by viewModels() private val emulationViewModel: EmulationViewModel by viewModels()
override fun onDestroy() {
stopForegroundService(this)
super.onDestroy()
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true Log.gameLaunched = true
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)
@ -125,10 +118,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
.apply() .apply()
} }
} }
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
@ -481,12 +470,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
activity.startActivity(launcher) activity.startActivity(launcher)
} }
fun stopForegroundService(activity: Activity) {
val startIntent = Intent(activity, ForegroundService::class.java)
startIntent.action = ForegroundService.ACTION_STOP
activity.startForegroundService(startIntent)
}
private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean { private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
if (view == null) { if (view == null) {
return true return true

View File

@ -34,7 +34,6 @@ import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R 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.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
@ -177,9 +176,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
} }
// Dismiss previous notifications (should not happen unless a crash occurred)
EmulationActivity.stopForegroundService(this)
setInsets() setInsets()
} }
@ -298,11 +294,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
super.onResume() super.onResume()
} }
override fun onDestroy() {
EmulationActivity.stopForegroundService(this)
super.onDestroy()
}
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root

View File

@ -1,70 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
/**
* A service that shows a permanent notification in the background to avoid the app getting
* cleared from memory by the system.
*/
class ForegroundService : Service() {
companion object {
const val EMULATION_RUNNING_NOTIFICATION = 0x1000
const val ACTION_STOP = "stop"
}
private fun showRunningNotification() {
// Intent is used to resume emulation if the notification is clicked
val contentIntent = PendingIntent.getActivity(
this,
0,
Intent(this, EmulationActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
)
val builder =
NotificationCompat.Builder(this, getString(R.string.emulation_notification_channel_id))
.setSmallIcon(R.drawable.ic_stat_notification_logo)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.emulation_notification_running))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setVibrate(null)
.setSound(null)
.setContentIntent(contentIntent)
startForeground(EMULATION_RUNNING_NOTIFICATION, builder.build())
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
showRunningNotification()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
return START_NOT_STICKY
}
if (intent.action == ACTION_STOP) {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelfResult(startId)
}
return START_STICKY
}
override fun onDestroy() {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
}
}

View File

@ -4,10 +4,6 @@
<!-- General application strings --> <!-- General application strings -->
<string name="app_name" translatable="false">yuzu</string> <string name="app_name" translatable="false">yuzu</string>
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string> <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string>
<string name="emulation_notification_channel_name">Emulation is Active</string>
<string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string>
<string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string>
<string name="emulation_notification_running">yuzu is running</string>
<string name="notice_notification_channel_name">Notices and errors</string> <string name="notice_notification_channel_name">Notices and errors</string>
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>

View File

@ -8,8 +8,8 @@
#include <jni.h> #include <jni.h>
#include "common/android/id_cache.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "id_cache.h"
namespace Common::Android { namespace Common::Android {

View File

@ -4,9 +4,9 @@
#include <jni.h> #include <jni.h>
#include "applets/software_keyboard.h" #include "applets/software_keyboard.h"
#include "common/android/id_cache.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/fs/fs_android.h" #include "common/fs/fs_android.h"
#include "id_cache.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
static JavaVM* s_java_vm; static JavaVM* s_java_vm;

View File

@ -44,7 +44,7 @@ bool IsContentUri(const std::string& path) {
return path.find(prefix) == 0; return path.find(prefix) == 0;
} }
int OpenContentUri(const std::string& filepath, OpenMode openmode) { s32 OpenContentUri(const std::string& filepath, OpenMode openmode) {
if (s_open_content_uri == nullptr) if (s_open_content_uri == nullptr)
return -1; return -1;
@ -63,7 +63,7 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode); return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode);
} }
std::uint64_t GetSize(const std::string& filepath) { u64 GetSize(const std::string& filepath) {
if (s_get_size == nullptr) { if (s_get_size == nullptr) {
return 0; return 0;
} }

View File

@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const
} else if (id == CPSR_REGISTER) { } else if (id == CPSR_REGISTER) {
return ValueToHex(context.pstate); return ValueToHex(context.pstate);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) { } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
return ValueToHex(fprs[id - D0_REGISTER][0]); return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
return ValueToHex(fprs[id - Q0_REGISTER]); return ValueToHex(fprs[id - Q0_REGISTER]);
} else if (id == FPSCR_REGISTER) { } else if (id == FPSCR_REGISTER) {
@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
} else if (id == CPSR_REGISTER) { } else if (id == CPSR_REGISTER) {
context.pstate = HexToValue<u32>(value); context.pstate = HexToValue<u32>(value);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) { } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0}; fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
fprs[id - Q0_REGISTER] = HexToValue<u128>(value); fprs[id - Q0_REGISTER] = HexToValue<u128>(value);
} else if (id == FPSCR_REGISTER) { } else if (id == FPSCR_REGISTER) {

View File

@ -532,6 +532,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
cache_bytes = 0; cache_bytes = 0;
} }
}; };
size_t old_vpage = (base_vaddress >> Memory::YUZU_PAGEBITS) - 1;
for (; page != page_end; ++page) { for (; page != page_end; ++page) {
CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page); CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page);
auto [asid_2, vpage] = ExtractCPUBacking(page); auto [asid_2, vpage] = ExtractCPUBacking(page);
@ -547,6 +548,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
memory_device_inter = registered_processes[asid_2.id]; memory_device_inter = registered_processes[asid_2.id];
} }
if (vpage != old_vpage + 1) [[unlikely]] {
release_pending();
}
old_vpage = vpage;
// Adds or subtracts 1, as count is a unsigned 8-bit value // Adds or subtracts 1, as count is a unsigned 8-bit value
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release); count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);

View File

@ -280,7 +280,7 @@ void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLE
u32 value{}; u32 value{};
std::memcpy(&value, raw_data + ArgOffset, ArgSize); std::memcpy(&value, raw_data + ArgOffset, ArgSize);
std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); std::get<ArgIndex>(args) = ctx.GetDomainHandler<typename ArgType::element_type>(value - 1);
return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {

View File

@ -65,6 +65,14 @@ struct ClientProcessId {
}; };
struct ProcessId { struct ProcessId {
explicit ProcessId() : pid() {}
explicit ProcessId(u64 p) : pid(p) {}
/* implicit */ ProcessId(const ClientProcessId& c) : pid(c.pid) {}
bool operator==(const ProcessId& rhs) const {
return pid == rhs.pid;
}
explicit operator bool() const { explicit operator bool() const {
return pid != 0; return pid != 0;
} }

View File

@ -36,7 +36,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService( server_manager->RegisterNamedService(
"hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); server_manager->RegisterNamedService("hidbus", std::make_shared<Hidbus>(system));
server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system)); server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system)); server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));

View File

@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hid_debug_server.h" #include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
@ -11,7 +12,6 @@
#include "hid_core/resources/touch_screen/gesture.h" #include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h" #include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Service::HID { namespace Service::HID {
@ -24,14 +24,14 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{0, nullptr, "DeactivateDebugPad"}, {0, nullptr, "DeactivateDebugPad"},
{1, nullptr, "SetDebugPadAutoPilotState"}, {1, nullptr, "SetDebugPadAutoPilotState"},
{2, nullptr, "UnsetDebugPadAutoPilotState"}, {2, nullptr, "UnsetDebugPadAutoPilotState"},
{10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"}, {10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"},
{11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"}, {11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"},
{12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"}, {12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"},
{13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"}, {13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"},
{14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"}, {14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"},
{15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"}, {15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"},
{16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"}, {16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"},
{17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"}, {17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"},
{20, nullptr, "DeactivateMouse"}, {20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"}, {21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"}, {22, nullptr, "UnsetMouseAutoPilotState"},
@ -47,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{60, nullptr, "ClearNpadSystemCommonPolicy"}, {60, nullptr, "ClearNpadSystemCommonPolicy"},
{61, nullptr, "DeactivateNpad"}, {61, nullptr, "DeactivateNpad"},
{62, nullptr, "ForceDisconnectNpad"}, {62, nullptr, "ForceDisconnectNpad"},
{91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"}, {91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"},
{110, nullptr, "DeactivateHomeButton"}, {110, nullptr, "DeactivateHomeButton"},
{111, nullptr, "SetHomeButtonAutoPilotState"}, {111, nullptr, "SetHomeButtonAutoPilotState"},
{112, nullptr, "UnsetHomeButtonAutoPilotState"}, {112, nullptr, "UnsetHomeButtonAutoPilotState"},
@ -160,169 +160,122 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
} }
IHidDebugServer::~IHidDebugServer() = default; IHidDebugServer::~IHidDebugServer() = default;
void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
Result IHidDebugServer::DeactivateTouchScreen() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetTouchScreen()->Deactivate(); R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate());
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) { Result IHidDebugServer::SetTouchScreenAutoPilotState(
InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) {
AutoPilotState auto_pilot{}; AutoPilotState auto_pilot{};
auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
const auto buffer = ctx.ReadBuffer();
auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size())); auto_pilot.count =
memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState)); static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size()));
memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(),
auto_pilot.count * sizeof(TouchState));
LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count); LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
const Result result = R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot));
GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) { Result IHidDebugServer::UnsetTouchScreenAutoPilotState() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState());
const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) { Result IHidDebugServer::GetTouchScreenConfiguration(
IPC::RequestParser rp{ctx}; Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
const auto applet_resource_user_id{rp.Pop<u64>()}; ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid);
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
*out_touchscreen_config, aruid.pid));
Core::HID::TouchScreenConfigurationForNx touchscreen_config{}; if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 &&
const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration( out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config, applet_resource_user_id); out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
} }
IPC::ResponseBuilder rb{ctx, 6}; R_SUCCEED();
rb.Push(result);
rb.PushRaw(touchscreen_config);
} }
void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) { Result IHidDebugServer::ProcessTouchScreenAutoTune() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune());
Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) { Result IHidDebugServer::ForceStopTouchScreenManagement() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
return;
} }
Result result = ResultSuccess;
bool is_touch_active{};
bool is_gesture_active{};
auto touch_screen = GetResourceManager()->GetTouchScreen(); auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture(); auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsTouchI2cManaged()) { if (firmware_settings->IsTouchI2cManaged()) {
result = touch_screen->IsActive(is_touch_active); bool is_touch_active{};
if (result.IsSuccess()) { bool is_gesture_active{};
result = gesture->IsActive(is_gesture_active); R_TRY(touch_screen->IsActive(is_touch_active));
R_TRY(gesture->IsActive(is_gesture_active));
if (is_touch_active) {
R_TRY(touch_screen->Deactivate());
} }
if (result.IsSuccess() && is_touch_active) { if (is_gesture_active) {
result = touch_screen->Deactivate(); R_TRY(gesture->Deactivate());
}
if (result.IsSuccess() && is_gesture_active) {
result = gesture->Deactivate();
} }
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) { Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
u32 basic_gesture_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
parameters.basic_gesture_id, parameters.applet_resource_user_id); basic_gesture_id, aruid.pid);
Result result = ResultSuccess;
auto touch_screen = GetResourceManager()->GetTouchScreen(); auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture(); auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) { if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
result = gesture->Activate(); R_TRY(gesture->Activate());
if (result.IsSuccess()) { R_TRY(gesture->Activate(aruid.pid, basic_gesture_id));
result = R_TRY(touch_screen->Activate());
gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id); R_TRY(touch_screen->Activate(aruid.pid));
}
if (result.IsSuccess()) {
result = touch_screen->Activate();
}
if (result.IsSuccess()) {
result = touch_screen->Activate(parameters.applet_resource_user_id);
}
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) { Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
bool is_touch_active{}; bool is_touch_active{};
bool is_gesture_active{}; bool is_gesture_active{};
R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active));
R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active));
Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active); *out_is_managed = is_touch_active || is_gesture_active;
if (result.IsSuccess()) { R_SUCCEED();
result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(is_touch_active | is_gesture_active);
} }
void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) { Result IHidDebugServer::DeactivateGesture() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetGesture()->Deactivate(); R_RETURN(GetResourceManager()->GetGesture()->Deactivate());
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {

View File

@ -3,7 +3,9 @@
#pragma once #pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Core { namespace Core {
class System; class System;
@ -20,15 +22,19 @@ public:
~IHidDebugServer() override; ~IHidDebugServer() override;
private: private:
void DeactivateTouchScreen(HLERequestContext& ctx); Result DeactivateTouchScreen();
void SetTouchScreenAutoPilotState(HLERequestContext& ctx); Result SetTouchScreenAutoPilotState(
void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx); InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer);
void GetTouchScreenConfiguration(HLERequestContext& ctx); Result UnsetTouchScreenAutoPilotState();
void ProcessTouchScreenAutoTune(HLERequestContext& ctx); Result GetTouchScreenConfiguration(
void ForceStopTouchScreenManagement(HLERequestContext& ctx); Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
void ForceRestartTouchScreenManagement(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void IsTouchScreenManaged(HLERequestContext& ctx); Result ProcessTouchScreenAutoTune();
void DeactivateGesture(HLERequestContext& ctx); Result ForceStopTouchScreenManagement();
Result ForceRestartTouchScreenManagement(u32 basic_gesture_id,
ClientAppletResourceUserId aruid);
Result IsTouchScreenManaged(Out<bool> out_is_managed);
Result DeactivateGesture();
std::shared_ptr<ResourceManager> GetResourceManager(); std::shared_ptr<ResourceManager> GetResourceManager();

View File

@ -9,6 +9,7 @@
#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -22,25 +23,25 @@ namespace Service::HID {
// (15ms, 66Hz) // (15ms, 66Hz)
constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
HidBus::HidBus(Core::System& system_) Hidbus::Hidbus(Core::System& system_)
: ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} { : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{1, &HidBus::GetBusHandle, "GetBusHandle"}, {1, C<&Hidbus::GetBusHandle>, "GetBusHandle"},
{2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"}, {2, C<&Hidbus::IsExternalDeviceConnected>, "IsExternalDeviceConnected"},
{3, &HidBus::Initialize, "Initialize"}, {3, C<&Hidbus::Initialize>, "Initialize"},
{4, &HidBus::Finalize, "Finalize"}, {4, C<&Hidbus::Finalize>, "Finalize"},
{5, &HidBus::EnableExternalDevice, "EnableExternalDevice"}, {5, C<&Hidbus::EnableExternalDevice>, "EnableExternalDevice"},
{6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"}, {6, C<&Hidbus::GetExternalDeviceId>, "GetExternalDeviceId"},
{7, &HidBus::SendCommandAsync, "SendCommandAsync"}, {7, C<&Hidbus::SendCommandAsync>, "SendCommandAsync"},
{8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"}, {8, C<&Hidbus::GetSendCommandAsynceResult>, "GetSendCommandAsynceResult"},
{9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"}, {9, C<&Hidbus::SetEventForSendCommandAsycResult>, "SetEventForSendCommandAsycResult"},
{10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, {10, C<&Hidbus::GetSharedMemoryHandle>, "GetSharedMemoryHandle"},
{11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"}, {11, C<&Hidbus::EnableJoyPollingReceiveMode>, "EnableJoyPollingReceiveMode"},
{12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"}, {12, C<&Hidbus::DisableJoyPollingReceiveMode>, "DisableJoyPollingReceiveMode"},
{13, nullptr, "GetPollingData"}, {13, nullptr, "GetPollingData"},
{14, &HidBus::SetStatusManagerType, "SetStatusManagerType"}, {14, C<&Hidbus::SetStatusManagerType>, "SetStatusManagerType"},
}; };
// clang-format on // clang-format on
@ -60,11 +61,11 @@ HidBus::HidBus(Core::System& system_)
hidbus_update_event); hidbus_update_event);
} }
HidBus::~HidBus() { Hidbus::~Hidbus() {
system.CoreTiming().UnscheduleEvent(hidbus_update_event); system.CoreTiming().UnscheduleEvent(hidbus_update_event);
} }
void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { void Hidbus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
if (is_hidbus_enabled) { if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) { for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initialized) { if (!devices[i].is_device_initialized) {
@ -84,7 +85,7 @@ void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
} }
} }
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { std::optional<std::size_t> Hidbus::GetDeviceIndexFromHandle(BusHandle handle) const {
for (std::size_t i = 0; i < devices.size(); ++i) { for (std::size_t i = 0; i < devices.size(); ++i) {
const auto& device_handle = devices[i].handle; const auto& device_handle = devices[i].handle;
if (handle.abstracted_pad_id == device_handle.abstracted_pad_id && if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
@ -98,20 +99,11 @@ std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) co
return std::nullopt; return std::nullopt;
} }
void HidBus::GetBusHandle(HLERequestContext& ctx) { Result Hidbus::GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
IPC::RequestParser rp{ctx}; Core::HID::NpadIdType npad_id, BusType bus_type,
struct Parameters { AppletResourceUserId aruid) {
Core::HID::NpadIdType npad_id; LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", npad_id,
INSERT_PADDING_WORDS_NOINIT(1); bus_type, aruid.pid);
BusType bus_type;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
bool is_handle_found = 0; bool is_handle_found = 0;
std::size_t handle_index = 0; std::size_t handle_index = 0;
@ -121,8 +113,8 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) {
if (!handle.is_valid) { if (!handle.is_valid) {
continue; continue;
} }
if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id && if (handle.player_number.As<Core::HID::NpadIdType>() == npad_id &&
handle.bus_type_id == static_cast<u8>(parameters.bus_type)) { handle.bus_type_id == static_cast<u8>(bus_type)) {
is_handle_found = true; is_handle_found = true;
handle_index = i; handle_index = i;
break; break;
@ -135,388 +127,231 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) {
if (devices[i].handle.is_valid) { if (devices[i].handle.is_valid) {
continue; continue;
} }
devices[i].handle = { devices[i].handle.raw = 0;
.abstracted_pad_id = static_cast<u8>(i), devices[i].handle.abstracted_pad_id.Assign(i);
.internal_index = static_cast<u8>(i), devices[i].handle.internal_index.Assign(i);
.player_number = static_cast<u8>(parameters.npad_id), devices[i].handle.player_number.Assign(static_cast<u8>(npad_id));
.bus_type_id = static_cast<u8>(parameters.bus_type), devices[i].handle.bus_type_id.Assign(static_cast<u8>(bus_type));
.is_valid = true, devices[i].handle.is_valid.Assign(true);
};
handle_index = i; handle_index = i;
break; break;
} }
} }
struct OutData { *out_is_valid = true;
bool is_valid; *out_bus_handle = devices[handle_index].handle;
INSERT_PADDING_BYTES(7); R_SUCCEED();
BusHandle handle;
};
static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
const OutData out_data{
.is_valid = true,
.handle = devices[handle_index].handle,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(out_data);
} }
void HidBus::IsExternalDeviceConnected(HLERequestContext& ctx) { Result Hidbus::IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"Called, abstracted_pad_id={}, bus_type={}, internal_index={}, " "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}", "player_number={}, is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
const bool is_attached = device->IsDeviceActivated();
IPC::ResponseBuilder rb{ctx, 3}; *out_is_connected = devices[device_index.value()].device->IsDeviceActivated();
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push(is_attached);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
} }
void HidBus::Initialize(HLERequestContext& ctx) { Result Hidbus::Initialize(BusHandle bus_handle, AppletResourceUserId aruid) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, abstracted_pad_id={} bus_type={} internal_index={} " "called, abstracted_pad_id={} bus_type={} internal_index={} "
"player_number={} is_valid={}, applet_resource_user_id={}", "player_number={} is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); bus_handle.player_number, bus_handle.is_valid, aruid.pid);
is_hidbus_enabled = true; is_hidbus_enabled = true;
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { const auto entry_index = devices[device_index.value()].handle.internal_index;
MakeDevice<RingController>(bus_handle_); auto& cur_entry = hidbus_status.entries[entry_index];
devices[device_index.value()].is_device_initialized = true;
devices[device_index.value()].device->ActivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = true;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
} else {
MakeDevice<HidbusStubbed>(bus_handle_);
devices[device_index.value()].is_device_initialized = true;
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
}
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Finalize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
auto& device = devices[device_index.value()].device;
devices[device_index.value()].is_device_initialized = false;
device->DeactivateDevice();
if (bus_handle.internal_index == 0 && Settings::values.enable_ring_controller) {
MakeDevice<RingController>(bus_handle);
devices[device_index.value()].is_device_initialized = true;
devices[device_index.value()].device->ActivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = true;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
} else {
MakeDevice<HidbusStubbed>(bus_handle);
devices[device_index.value()].is_device_initialized = true;
cur_entry.is_in_focus = true; cur_entry.is_in_focus = true;
cur_entry.is_connected = false; cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess; cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false; cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false; cur_entry.is_polling_mode = false;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
IPC::ResponseBuilder rb{ctx, 2}; sizeof(hidbus_status));
rb.Push(ResultUnknown); R_SUCCEED();
return;
} }
void HidBus::EnableExternalDevice(HLERequestContext& ctx) { Result Hidbus::Finalize(BusHandle bus_handle, AppletResourceUserId aruid) {
IPC::RequestParser rp{ctx}; LOG_INFO(Service_HID,
struct Parameters { "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
bool enable; "player_number={}, is_valid={}, applet_resource_user_id={}",
INSERT_PADDING_BYTES_NOINIT(7); bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
BusHandle bus_handle; bus_handle.player_number, bus_handle.is_valid, aruid.pid);
u64 inval;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()}; const auto device_index = GetDeviceIndexFromHandle(bus_handle);
R_UNLESS(device_index.has_value(), ResultUnknown);
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
auto& device = devices[device_index.value()].device;
devices[device_index.value()].is_device_initialized = false;
device->DeactivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
R_SUCCEED();
}
Result Hidbus::EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
AppletResourceUserId aruid) {
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
parameters.enable, parameters.bus_handle.abstracted_pad_id, is_enabled, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
parameters.bus_handle.bus_type_id, parameters.bus_handle.internal_index, bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid, inval,
parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, aruid.pid);
parameters.applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
auto& device = devices[device_index.value()].device; devices[device_index.value()].device->Enable(is_enabled);
device->Enable(parameters.enable); R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
} }
void HidBus::GetExternalDeviceId(HLERequestContext& ctx) { Result Hidbus::GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
u32 device_id = device->GetDeviceId();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(device_id);
return;
}
LOG_ERROR(Service_HID, "Invalid handle"); *out_device_id = devices[device_index.value()].device->GetDeviceId();
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultUnknown);
return;
} }
void HidBus::SendCommandAsync(HLERequestContext& ctx) { Result Hidbus::SendCommandAsync(BusHandle bus_handle,
IPC::RequestParser rp{ctx}; InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
const auto data = ctx.ReadBuffer();
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}", "player_number={}, is_valid={}",
data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, buffer_data.size(), bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
auto& device = devices[device_index.value()].device;
device->SetCommand(data);
IPC::ResponseBuilder rb{ctx, 2}; devices[device_index.value()].device->SetCommand(buffer_data);
rb.Push(ResultSuccess); R_SUCCEED();
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}; };
void HidBus::GetSendCommandAsynceResult(HLERequestContext& ctx) { Result Hidbus::GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
IPC::RequestParser rp{ctx}; OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data) {
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
const std::vector<u8> data = device->GetReply();
const u64 data_size = ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 4}; *out_data_size = devices[device_index.value()].device->GetReply(out_buffer_data);
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push<u64>(data_size);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}; };
void HidBus::SetEventForSendCommandAsycResult(HLERequestContext& ctx) { Result Hidbus::SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
IPC::RequestParser rp{ctx}; BusHandle bus_handle) {
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device->GetSendCommandAsycEvent());
return;
}
LOG_ERROR(Service_HID, "Invalid handle"); *out_event = &devices[device_index.value()].device->GetSendCommandAsycEvent();
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultUnknown);
return;
}; };
void HidBus::GetSharedMemoryHandle(HLERequestContext& ctx) { Result Hidbus::GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory) {
LOG_DEBUG(Service_HID, "called"); LOG_DEBUG(Service_HID, "called");
IPC::ResponseBuilder rb{ctx, 2, 1}; *out_shared_memory = &system.Kernel().GetHidBusSharedMem();
rb.Push(ResultSuccess); R_SUCCEED();
rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
} }
void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) { Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
IPC::RequestParser rp{ctx}; BusHandle bus_handle,
const auto t_mem_size{rp.Pop<u32>()}; InCopyHandle<Kernel::KTransferMemory> t_mem) {
const auto t_mem_handle{ctx.GetCopyHandle(0)};
const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
ASSERT_MSG(t_mem->GetSize() == t_mem_size, "t_mem has incorrect size");
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, " "called, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
"internal_index={}, player_number={}, is_valid={}", "internal_index={}, player_number={}, is_valid={}",
t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, polling_mode, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
auto& device = devices[device_index.value()].device;
device->SetPollingMode(polling_mode_);
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
IPC::ResponseBuilder rb{ctx, 2}; auto& device = devices[device_index.value()].device;
rb.Push(ResultSuccess); device->SetPollingMode(polling_mode);
return; device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
} R_SUCCEED();
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
} }
void HidBus::DisableJoyPollingReceiveMode(HLERequestContext& ctx) { Result Hidbus::DisableJoyPollingReceiveMode(BusHandle bus_handle) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
auto& device = devices[device_index.value()].device;
device->DisablePollingMode();
IPC::ResponseBuilder rb{ctx, 2}; auto& device = devices[device_index.value()].device;
rb.Push(ResultSuccess); device->DisablePollingMode();
return; R_SUCCEED();
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
} }
void HidBus::SetStatusManagerType(HLERequestContext& ctx) { Result Hidbus::SetStatusManagerType(StatusManagerType manager_type) {
IPC::RequestParser rp{ctx};
const auto manager_type{rp.PopEnum<StatusManagerType>()};
LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type); LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}; };
} // namespace Service::HID } // namespace Service::HID

View File

@ -5,8 +5,10 @@
#include <functional> #include <functional>
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
#include "hid_core/hidbus/hidbus_base.h" #include "hid_core/hidbus/hidbus_base.h"
namespace Core::Timing { namespace Core::Timing {
@ -19,10 +21,10 @@ class System;
namespace Service::HID { namespace Service::HID {
class HidBus final : public ServiceFramework<HidBus> { class Hidbus final : public ServiceFramework<Hidbus> {
public: public:
explicit HidBus(Core::System& system_); explicit Hidbus(Core::System& system_);
~HidBus() override; ~Hidbus() override;
private: private:
static const std::size_t max_number_of_handles = 0x13; static const std::size_t max_number_of_handles = 0x13;
@ -41,7 +43,7 @@ private:
}; };
// This is nn::hidbus::BusType // This is nn::hidbus::BusType
enum class BusType : u32 { enum class BusType : u64 {
LeftJoyRail, LeftJoyRail,
RightJoyRail, RightJoyRail,
InternalBus, // Lark microphone InternalBus, // Lark microphone
@ -51,11 +53,15 @@ private:
// This is nn::hidbus::BusHandle // This is nn::hidbus::BusHandle
struct BusHandle { struct BusHandle {
u32 abstracted_pad_id; union {
u8 internal_index; u64 raw{};
u8 player_number;
u8 bus_type_id; BitField<0, 32, u64> abstracted_pad_id;
bool is_valid; BitField<32, 8, u64> internal_index;
BitField<40, 8, u64> player_number;
BitField<48, 8, u64> bus_type_id;
BitField<56, 1, u64> is_valid;
};
}; };
static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size"); static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
@ -94,19 +100,38 @@ private:
std::unique_ptr<HidbusBase> device{nullptr}; std::unique_ptr<HidbusBase> device{nullptr};
}; };
void GetBusHandle(HLERequestContext& ctx); Result GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
void IsExternalDeviceConnected(HLERequestContext& ctx); Core::HID::NpadIdType npad_id, BusType bus_type,
void Initialize(HLERequestContext& ctx); AppletResourceUserId aruid);
void Finalize(HLERequestContext& ctx);
void EnableExternalDevice(HLERequestContext& ctx); Result IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle);
void GetExternalDeviceId(HLERequestContext& ctx);
void SendCommandAsync(HLERequestContext& ctx); Result Initialize(BusHandle bus_handle, AppletResourceUserId aruid);
void GetSendCommandAsynceResult(HLERequestContext& ctx);
void SetEventForSendCommandAsycResult(HLERequestContext& ctx); Result Finalize(BusHandle bus_handle, AppletResourceUserId aruid);
void GetSharedMemoryHandle(HLERequestContext& ctx);
void EnableJoyPollingReceiveMode(HLERequestContext& ctx); Result EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
void DisableJoyPollingReceiveMode(HLERequestContext& ctx); AppletResourceUserId aruid);
void SetStatusManagerType(HLERequestContext& ctx);
Result GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle);
Result SendCommandAsync(BusHandle bus_handle, InBuffer<BufferAttr_HipcAutoSelect> buffer_data);
Result GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data);
Result SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
BusHandle bus_handle);
Result GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory);
Result EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
BusHandle bus_handle,
InCopyHandle<Kernel::KTransferMemory> t_mem);
Result DisableJoyPollingReceiveMode(BusHandle bus_handle);
Result SetStatusManagerType(StatusManagerType manager_type);
void UpdateHidbus(std::chrono::nanoseconds ns_late); void UpdateHidbus(std::chrono::nanoseconds ns_late);
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;

View File

@ -9,6 +9,7 @@
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/irs.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/memory.h" #include "core/memory.h"
@ -28,24 +29,24 @@ namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, {302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"},
{303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"}, {303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"},
{304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"}, {304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"},
{305, &IRS::StopImageProcessor, "StopImageProcessor"}, {305, C<&IRS::StopImageProcessor>, "StopImageProcessor"},
{306, &IRS::RunMomentProcessor, "RunMomentProcessor"}, {306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"},
{307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"}, {307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"},
{308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"}, {308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"},
{309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"}, {309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"},
{310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"}, {310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"},
{311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"}, {311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"},
{312, &IRS::RunPointingProcessor, "RunPointingProcessor"}, {312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"},
{313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"}, {313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"},
{314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"}, {314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"},
{315, &IRS::SetFunctionLevel, "SetFunctionLevel"}, {315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"},
{316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"}, {316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"},
{317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"}, {317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"},
{318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"}, {318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"},
{319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"}, {319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"},
}; };
// clang-format on // clang-format on
@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
} }
IRS::~IRS() = default; IRS::~IRS() = default;
void IRS::ActivateIrsensor(HLERequestContext& ctx) { Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
const auto applet_resource_user_id{rp.Pop<u64>()}; R_SUCCEED();
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IRS::DeactivateIrsensor(HLERequestContext& ctx) { Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
const auto applet_resource_user_id{rp.Pop<u64>()}; R_SUCCEED();
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) { Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
const auto applet_resource_user_id{rp.Pop<u64>()}; LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid);
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); *out_shared_memory = &system.Kernel().GetIrsSharedMem();
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem());
} }
void IRS::StopImageProcessor(HLERequestContext& ctx) { Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Stop Image processor
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Stop Image processor
rb.Push(result); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
} }
void IRS::RunMomentProcessor(HLERequestContext& ctx) { Result IRS::RunMomentProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedMomentProcessorConfig& processor_config) {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetConfig(parameters.processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR);
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::RunClusteringProcessor(HLERequestContext& ctx) { Result IRS::RunClusteringProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ClusteringProcessor>(parameters.camera_handle); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
image_transfer_processor.SetConfig(parameters.processor_config); Common::Input::PollingMode::IR);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::RunImageTransferProcessor(HLERequestContext& ctx) { Result IRS::RunImageTransferProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
Core::IrSensor::IrCameraHandle camera_handle; u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()}; ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
LOG_INFO(Service_IRS, LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
"applet_resource_user_id={}", "applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(),
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ImageTransferProcessor>(parameters.camera_handle); image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
image_transfer_processor.SetConfig(parameters.processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); Common::Input::PollingMode::IR);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) { Result IRS::GetImageTransferProcessorState(
IPC::RequestParser rp{ctx}; Out<Core::IrSensor::ImageTransferProcessorState> out_state,
struct Parameters { Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
Core::IrSensor::IrCameraHandle camera_handle; OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2}; R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor,
rb.Push(InvalidProcessorState); InvalidProcessorState);
return;
}
std::vector<u8> data{}; *out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data);
const auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
const auto& state = image_transfer_processor.GetState(data);
ctx.WriteBuffer(data); R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(state);
} }
void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) { Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
struct Parameters { ClientAppletResourceUserId aruid) {
Core::IrSensor::IrCameraHandle camera_handle; LOG_WARNING(Service_IRS,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
INSERT_PADDING_WORDS_NOINIT(1); "applet_resource_user_id={}",
u64 applet_resource_user_id; camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
}; processor_config.required_mcu_version.major,
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); processor_config.required_mcu_version.minor, aruid.pid);
const auto parameters{rp.PopRaw<Parameters>()}; R_TRY(IsIrCameraHandleValid(camera_handle));
LOG_WARNING( auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
Service_IRS, MakeProcessor<TeraPluginProcessor>(camera_handle, device);
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle);
"applet_resource_user_id={}", image_transfer_processor.SetConfig(processor_config);
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, Common::Input::PollingMode::IR);
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_SUCCEED();
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
IPC::RequestParser rp{ctx}; Core::HID::NpadIdType npad_id) {
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId);
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && *out_camera_handle = {
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Service::HID::ResultInvalidNpadId);
return;
}
Core::IrSensor::IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None, .npad_type = Core::HID::NpadStyleIndex::None,
}; };
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
camera_handle.npad_id, camera_handle.npad_type); out_camera_handle->npad_id, out_camera_handle->npad_type);
IPC::ResponseBuilder rb{ctx, 3}; R_SUCCEED();
rb.Push(ResultSuccess);
rb.PushRaw(camera_handle);
} }
void IRS::RunPointingProcessor(HLERequestContext& ctx) { Result IRS::RunPointingProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; ClientAppletResourceUserId aruid) {
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id); processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); MakeProcessor<PointingProcessor>(camera_handle, device);
MakeProcessor<PointingProcessor>(camera_handle, device); auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetConfig(processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR);
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::SuspendImageProcessor(HLERequestContext& ctx) { Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Suspend image processor
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Suspend image processor
rb.Push(result);
R_SUCCEED();
} }
void IRS::CheckFirmwareVersion(HLERequestContext& ctx) { Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedMcuVersion mcu_version,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; ClientAppletResourceUserId aruid) {
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major,
mcu_version.minor); mcu_version.minor);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Check firmware version
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Check firmware version
rb.Push(result);
R_SUCCEED();
} }
void IRS::SetFunctionLevel(HLERequestContext& ctx) { Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedFunctionLevel function_level,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; ClientAppletResourceUserId aruid) {
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid);
applet_resource_user_id);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Set Function level
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Set Function level
rb.Push(result);
R_SUCCEED();
} }
void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) { Result IRS::RunImageTransferExProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
Core::IrSensor::IrCameraHandle camera_handle; u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()}; ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
LOG_INFO(Service_IRS, LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, " "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}", "applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid);
parameters.transfer_memory_size, parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ImageTransferProcessor>(parameters.camera_handle); image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
image_transfer_processor.SetConfig(parameters.processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); Common::Input::PollingMode::IR);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::RunIrLedProcessor(HLERequestContext& ctx) { Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedIrLedProcessorConfig processor_config,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; ClientAppletResourceUserId aruid) {
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
"applet_resource_user_id={}", "applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
processor_config.required_mcu_version.major, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id); processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); MakeProcessor<IrLedProcessor>(camera_handle, device);
MakeProcessor<IrLedProcessor>(camera_handle, device); auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetConfig(processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR);
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::StopImageProcessorAsync(HLERequestContext& ctx) { Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Stop image processor async
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Stop image processor async
rb.Push(result); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
} }
void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) { Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
parameters.function_level.function_level, parameters.applet_resource_user_id); function_level.function_level, aruid.pid);
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/irs_types.h"
@ -35,26 +36,73 @@ private:
}; };
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(HLERequestContext& ctx); Result ActivateIrsensor(ClientAppletResourceUserId aruid);
void DeactivateIrsensor(HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx); Result DeactivateIrsensor(ClientAppletResourceUserId aruid);
void StopImageProcessor(HLERequestContext& ctx);
void RunMomentProcessor(HLERequestContext& ctx); Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
void RunClusteringProcessor(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void RunImageTransferProcessor(HLERequestContext& ctx); Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
void GetImageTransferProcessorState(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void RunTeraPluginProcessor(HLERequestContext& ctx);
void GetNpadIrCameraHandle(HLERequestContext& ctx); Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle,
void RunPointingProcessor(HLERequestContext& ctx); ClientAppletResourceUserId aruid,
void SuspendImageProcessor(HLERequestContext& ctx); const Core::IrSensor::PackedMomentProcessorConfig& processor_config);
void CheckFirmwareVersion(HLERequestContext& ctx);
void SetFunctionLevel(HLERequestContext& ctx); Result RunClusteringProcessor(
void RunImageTransferExProcessor(HLERequestContext& ctx); Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
void RunIrLedProcessor(HLERequestContext& ctx); const Core::IrSensor::PackedClusteringProcessorConfig& processor_config);
void StopImageProcessorAsync(HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx); Result RunImageTransferProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result GetImageTransferProcessorState(
Out<Core::IrSensor::ImageTransferProcessorState> out_state,
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data);
Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
Core::HID::NpadIdType npad_id);
Result RunPointingProcessor(
Core::IrSensor::IrCameraHandle camera_handle,
const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
ClientAppletResourceUserId aruid);
Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedMcuVersion mcu_version,
ClientAppletResourceUserId aruid);
Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result RunImageTransferExProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedIrLedProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle); const Core::IrSensor::IrCameraHandle& camera_handle);

View File

@ -299,8 +299,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() {
if (GetManager()->IsDomain()) { if (GetManager()->IsDomain()) {
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
for (auto& object : outgoing_domain_objects) { for (auto& object : outgoing_domain_objects) {
GetManager()->AppendDomainHandler(std::move(object)); if (object) {
cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount()); GetManager()->AppendDomainHandler(std::move(object));
cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
} else {
cmd_buf[current_offset++] = 0;
}
} }
} }

View File

@ -123,6 +123,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
vm.va_range_end = params.va_range_end; vm.va_range_end = params.va_range_end;
} }
const u64 max_big_page_bits = Common::Log2Ceil64(vm.va_range_end);
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
@ -132,8 +134,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits, gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split,
VM::PAGE_SIZE_BITS); vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
system.GPU().InitAddressSpace(*gmmu); system.GPU().InitAddressSpace(*gmmu);
vm.initialised = true; vm.initialised = true;

View File

@ -5,6 +5,7 @@
#include "common/hex_util.h" #include "common/hex_util.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/arm/debug.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_page_table.h"
@ -63,7 +64,10 @@ void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64
return; return;
} }
system.ApplicationMemory().WriteBlock(address, data, size); if (system.ApplicationMemory().WriteBlock(address, data, size) &&
system.ApplicationProcess()->Is64Bit()) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
}
} }
u64 StandardVmCallbacks::HidKeysDown() { u64 StandardVmCallbacks::HidKeysDown() {

View File

@ -160,7 +160,7 @@ public:
} }
// Returns a reply from a command // Returns a reply from a command
virtual std::vector<u8> GetReply() const { virtual u64 GetReply(std::span<u8> out_data) const {
return {}; return {};
} }

View File

@ -90,32 +90,32 @@ u8 RingController::GetDeviceId() const {
return device_id; return device_id;
} }
std::vector<u8> RingController::GetReply() const { u64 RingController::GetReply(std::span<u8> out_data) const {
const RingConCommands current_command = command; const RingConCommands current_command = command;
switch (current_command) { switch (current_command) {
case RingConCommands::GetFirmwareVersion: case RingConCommands::GetFirmwareVersion:
return GetFirmwareVersionReply(); return GetFirmwareVersionReply(out_data);
case RingConCommands::ReadId: case RingConCommands::ReadId:
return GetReadIdReply(); return GetReadIdReply(out_data);
case RingConCommands::c20105: case RingConCommands::c20105:
return GetC020105Reply(); return GetC020105Reply(out_data);
case RingConCommands::ReadUnkCal: case RingConCommands::ReadUnkCal:
return GetReadUnkCalReply(); return GetReadUnkCalReply(out_data);
case RingConCommands::ReadFactoryCal: case RingConCommands::ReadFactoryCal:
return GetReadFactoryCalReply(); return GetReadFactoryCalReply(out_data);
case RingConCommands::ReadUserCal: case RingConCommands::ReadUserCal:
return GetReadUserCalReply(); return GetReadUserCalReply(out_data);
case RingConCommands::ReadRepCount: case RingConCommands::ReadRepCount:
return GetReadRepCountReply(); return GetReadRepCountReply(out_data);
case RingConCommands::ReadTotalPushCount: case RingConCommands::ReadTotalPushCount:
return GetReadTotalPushCountReply(); return GetReadTotalPushCountReply(out_data);
case RingConCommands::ResetRepCount: case RingConCommands::ResetRepCount:
return GetResetRepCountReply(); return GetResetRepCountReply(out_data);
case RingConCommands::SaveCalData: case RingConCommands::SaveCalData:
return GetSaveDataReply(); return GetSaveDataReply(out_data);
default: default:
return GetErrorReply(); return GetErrorReply(out_data);
} }
} }
@ -163,16 +163,16 @@ bool RingController::SetCommand(std::span<const u8> data) {
} }
} }
std::vector<u8> RingController::GetFirmwareVersionReply() const { u64 RingController::GetFirmwareVersionReply(std::span<u8> out_data) const {
const FirmwareVersionReply reply{ const FirmwareVersionReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.firmware = version, .firmware = version,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadIdReply() const { u64 RingController::GetReadIdReply(std::span<u8> out_data) const {
// The values are hardcoded from a real joycon // The values are hardcoded from a real joycon
const ReadIdReply reply{ const ReadIdReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
@ -184,83 +184,83 @@ std::vector<u8> RingController::GetReadIdReply() const {
.id_h_x4 = 8245, .id_h_x4 = 8245,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetC020105Reply() const { u64 RingController::GetC020105Reply(std::span<u8> out_data) const {
const Cmd020105Reply reply{ const Cmd020105Reply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = 1, .data = 1,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadUnkCalReply() const { u64 RingController::GetReadUnkCalReply(std::span<u8> out_data) const {
const ReadUnkCalReply reply{ const ReadUnkCalReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = 0, .data = 0,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadFactoryCalReply() const { u64 RingController::GetReadFactoryCalReply(std::span<u8> out_data) const {
const ReadFactoryCalReply reply{ const ReadFactoryCalReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.calibration = factory_calibration, .calibration = factory_calibration,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadUserCalReply() const { u64 RingController::GetReadUserCalReply(std::span<u8> out_data) const {
const ReadUserCalReply reply{ const ReadUserCalReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.calibration = user_calibration, .calibration = user_calibration,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadRepCountReply() const { u64 RingController::GetReadRepCountReply(std::span<u8> out_data) const {
const GetThreeByteReply reply{ const GetThreeByteReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = {total_rep_count, 0, 0}, .data = {total_rep_count, 0, 0},
.crc = GetCrcValue({total_rep_count, 0, 0, 0}), .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadTotalPushCountReply() const { u64 RingController::GetReadTotalPushCountReply(std::span<u8> out_data) const {
const GetThreeByteReply reply{ const GetThreeByteReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = {total_push_count, 0, 0}, .data = {total_push_count, 0, 0},
.crc = GetCrcValue({total_push_count, 0, 0, 0}), .crc = GetCrcValue({total_push_count, 0, 0, 0}),
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetResetRepCountReply() const { u64 RingController::GetResetRepCountReply(std::span<u8> out_data) const {
return GetReadRepCountReply(); return GetReadRepCountReply(out_data);
} }
std::vector<u8> RingController::GetSaveDataReply() const { u64 RingController::GetSaveDataReply(std::span<u8> out_data) const {
const StatusReply reply{ const StatusReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetErrorReply() const { u64 RingController::GetErrorReply(std::span<u8> out_data) const {
const ErrorReply reply{ const ErrorReply reply{
.status = DataValid::BadCRC, .status = DataValid::BadCRC,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
u8 RingController::GetCrcValue(const std::vector<u8>& data) const { u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
@ -281,12 +281,11 @@ u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
} }
template <typename T> template <typename T>
std::vector<u8> RingController::GetDataVector(const T& reply) const { u64 RingController::GetData(const T& reply, std::span<u8> out_data) const {
static_assert(std::is_trivially_copyable_v<T>); static_assert(std::is_trivially_copyable_v<T>);
std::vector<u8> data; const auto data_size = static_cast<u64>(std::min(sizeof(reply), out_data.size()));
data.resize(sizeof(reply)); std::memcpy(out_data.data(), &reply, data_size);
std::memcpy(data.data(), &reply, sizeof(reply)); return data_size;
return data;
} }
} // namespace Service::HID } // namespace Service::HID

View File

@ -34,7 +34,7 @@ public:
bool SetCommand(std::span<const u8> data) override; bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command // Returns a reply from a command
std::vector<u8> GetReply() const override; u64 GetReply(std::span<u8> data) const override;
private: private:
// These values are obtained from a real ring controller // These values are obtained from a real ring controller
@ -184,44 +184,44 @@ private:
RingConData GetSensorValue() const; RingConData GetSensorValue() const;
// Returns 8 byte reply with firmware version // Returns 8 byte reply with firmware version
std::vector<u8> GetFirmwareVersionReply() const; u64 GetFirmwareVersionReply(std::span<u8> out_data) const;
// Returns 16 byte reply with ID values // Returns 16 byte reply with ID values
std::vector<u8> GetReadIdReply() const; u64 GetReadIdReply(std::span<u8> out_data) const;
// (STUBBED) Returns 8 byte reply // (STUBBED) Returns 8 byte reply
std::vector<u8> GetC020105Reply() const; u64 GetC020105Reply(std::span<u8> out_data) const;
// (STUBBED) Returns 8 byte empty reply // (STUBBED) Returns 8 byte empty reply
std::vector<u8> GetReadUnkCalReply() const; u64 GetReadUnkCalReply(std::span<u8> out_data) const;
// Returns 20 byte reply with factory calibration values // Returns 20 byte reply with factory calibration values
std::vector<u8> GetReadFactoryCalReply() const; u64 GetReadFactoryCalReply(std::span<u8> out_data) const;
// Returns 20 byte reply with user calibration values // Returns 20 byte reply with user calibration values
std::vector<u8> GetReadUserCalReply() const; u64 GetReadUserCalReply(std::span<u8> out_data) const;
// Returns 8 byte reply // Returns 8 byte reply
std::vector<u8> GetReadRepCountReply() const; u64 GetReadRepCountReply(std::span<u8> out_data) const;
// Returns 8 byte reply // Returns 8 byte reply
std::vector<u8> GetReadTotalPushCountReply() const; u64 GetReadTotalPushCountReply(std::span<u8> out_data) const;
// Returns 8 byte reply // Returns 8 byte reply
std::vector<u8> GetResetRepCountReply() const; u64 GetResetRepCountReply(std::span<u8> out_data) const;
// Returns 4 byte save data reply // Returns 4 byte save data reply
std::vector<u8> GetSaveDataReply() const; u64 GetSaveDataReply(std::span<u8> out_data) const;
// Returns 8 byte error reply // Returns 8 byte error reply
std::vector<u8> GetErrorReply() const; u64 GetErrorReply(std::span<u8> out_data) const;
// Returns 8 bit redundancy check from provided data // Returns 8 bit redundancy check from provided data
u8 GetCrcValue(const std::vector<u8>& data) const; u8 GetCrcValue(const std::vector<u8>& data) const;
// Converts structs to an u8 vector equivalent // Converts structs to an u8 vector equivalent
template <typename T> template <typename T>
std::vector<u8> GetDataVector(const T& reply) const; u64 GetData(const T& reply, std::span<u8> out_data) const;
RingConCommands command{RingConCommands::Error}; RingConCommands command{RingConCommands::Error};

View File

@ -38,7 +38,7 @@ u8 Starlink::GetDeviceId() const {
return DEVICE_ID; return DEVICE_ID;
} }
std::vector<u8> Starlink::GetReply() const { u64 Starlink::GetReply(std::span<u8> out_data) const {
return {}; return {};
} }

View File

@ -31,7 +31,7 @@ public:
bool SetCommand(std::span<const u8> data) override; bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command // Returns a reply from a command
std::vector<u8> GetReply() const override; u64 GetReply(std::span<u8> out_data) const override;
}; };
} // namespace Service::HID } // namespace Service::HID

View File

@ -38,7 +38,7 @@ u8 HidbusStubbed::GetDeviceId() const {
return DEVICE_ID; return DEVICE_ID;
} }
std::vector<u8> HidbusStubbed::GetReply() const { u64 HidbusStubbed::GetReply(std::span<u8> out_data) const {
return {}; return {};
} }

View File

@ -31,7 +31,7 @@ public:
bool SetCommand(std::span<const u8> data) override; bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command // Returns a reply from a command
std::vector<u8> GetReply() const override; u64 GetReply(std::span<u8> out_data) const override;
}; };
} // namespace Service::HID } // namespace Service::HID

View File

@ -145,9 +145,8 @@ void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_m
} }
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const { std::span<u8> data) const {
const auto size = GetDataSize(current_config.trimming_format); const auto size = std::min(GetDataSize(current_config.trimming_format), data.size());
data.resize(size);
system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
return processor_state; return processor_state;
} }

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include <span>
#include "common/typed_address.h" #include "common/typed_address.h"
#include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/irs_types.h"
#include "hid_core/irsensor/processor_base.h" #include "hid_core/irsensor/processor_base.h"
@ -39,7 +41,7 @@ public:
// Transfer memory where the image data will be stored // Transfer memory where the image data will be stored
void SetTransferMemoryAddress(Common::ProcessAddress t_mem); void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const;
private: private:
// This is nn::irsensor::ImageTransferProcessorConfig // This is nn::irsensor::ImageTransferProcessorConfig

View File

@ -57,7 +57,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (data->flag.is_assigned) { if (data == nullptr || !data->flag.is_assigned) {
continue; continue;
} }
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];

View File

@ -131,7 +131,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
if (!data->flag.is_assigned) { if (data == nullptr || !data->flag.is_assigned) {
continue; continue;
} }
@ -463,13 +463,13 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock lock{*applet_resource_holder.shared_mutex}; std::scoped_lock lock{*applet_resource_holder.shared_mutex};
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
const auto aruid = data->aruid;
if (!data->flag.is_assigned) { if (data == nullptr || !data->flag.is_assigned) {
continue; continue;
} }
bool is_set{}; bool is_set{};
const auto aruid = data->aruid;
npad_resource.IsSupportedNpadStyleSet(is_set, aruid); npad_resource.IsSupportedNpadStyleSet(is_set, aruid);
// Wait until style is defined // Wait until style is defined
if (!is_set) { if (!is_set) {

View File

@ -28,142 +28,148 @@ void SixAxis::OnRelease() {}
void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock shared_lock{*shared_mutex}; std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
if (data == nullptr || !data->flag.is_assigned) { for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
return; const auto* data = applet_resource->GetAruidDataByIndex(aruid_index);
}
if (!IsControllerActivated()) { if (data == nullptr || !data->flag.is_assigned) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
auto& controller = controller_data[i];
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
continue; continue;
} }
const auto& motion_state = controller.device->GetMotions(); if (!IsControllerActivated()) {
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; return;
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 = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// 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, for (std::size_t i = 0; i < controller_data.size(); ++i) {
const Core::HID::ControllerMotion& hid_state) { NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
using namespace std::literals::chrono_literals; auto& controller = controller_data[i];
static constexpr Core::HID::SixAxisSensorState default_motion_state = { const auto& controller_type = controller.device->GetNpadStyleIndex();
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f}, if (!data->flag.enable_six_axis_sensor) {
.orientation = continue;
{ }
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0}, if (controller_type == Core::HID::NpadStyleIndex::None ||
Common::Vec3f{0, 0, 1.0f}, !controller.device->IsConnected()) {
}, continue;
.attribute = {1}, }
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 = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// 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;
}; };
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state; switch (controller_type) {
return; case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::Fullkey:
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;
} }
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state; sixaxis_fullkey_state.sampling_number =
return; sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.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.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
} }
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) { sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
case Core::HID::NpadStyleIndex::None: sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
ASSERT(false); sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
break; sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
case Core::HID::NpadStyleIndex::Fullkey:
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.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.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.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
}
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
} }
} }

View File

@ -63,7 +63,7 @@ Result TouchResource::ActivateTouch(u64 aruid) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index]; TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {}; touch_data = {};
continue; continue;
} }
@ -124,7 +124,7 @@ Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index]; TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {}; touch_data = {};
continue; continue;
} }
@ -324,7 +324,7 @@ Result TouchResource::SetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& data = aruid_data[aruid_index]; TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue; continue;
} }
if (aruid != data.aruid) { if (aruid != data.aruid) {
@ -344,7 +344,7 @@ Result TouchResource::GetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
const TouchAruidData& data = aruid_data[aruid_index]; const TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue; continue;
} }
if (aruid != data.aruid) { if (aruid != data.aruid) {

View File

@ -13,102 +13,20 @@ Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
Scheduler::~Scheduler() = default; Scheduler::~Scheduler() = default;
void Scheduler::Init() {
master_control = Common::Fiber::ThreadToFiber();
}
void Scheduler::Resume() {
bool nothing_pending;
do {
nothing_pending = true;
current_fifo = nullptr;
{
std::unique_lock lk(scheduling_guard);
size_t num_iters = gpfifos.size();
for (size_t i = 0; i < num_iters; i++) {
size_t current_id = (current_fifo_rotation_id + i) % gpfifos.size();
auto& fifo = gpfifos[current_id];
if (!fifo.is_active) {
continue;
}
std::scoped_lock lk2(fifo.guard);
if (!fifo.pending_work.empty() || fifo.working.load(std::memory_order_acquire)) {
current_fifo = &fifo;
current_fifo_rotation_id = current_id;
nothing_pending = false;
break;
}
}
}
if (current_fifo) {
Common::Fiber::YieldTo(master_control, *current_fifo->context);
current_fifo = nullptr;
}
} while (!nothing_pending);
}
void Scheduler::Yield() {
ASSERT(current_fifo != nullptr);
Common::Fiber::YieldTo(current_fifo->context, *master_control);
gpu.BindChannel(current_fifo->bind_id);
}
void Scheduler::Push(s32 channel, CommandList&& entries) { void Scheduler::Push(s32 channel, CommandList&& entries) {
std::unique_lock lk(scheduling_guard); std::unique_lock lk(scheduling_guard);
auto it = channel_gpfifo_ids.find(channel); auto it = channels.find(channel);
ASSERT(it != channel_gpfifo_ids.end()); ASSERT(it != channels.end());
auto gpfifo_id = it->second; auto channel_state = it->second;
auto& fifo = gpfifos[gpfifo_id]; gpu.BindChannel(channel_state->bind_id);
{ channel_state->dma_pusher->Push(std::move(entries));
std::scoped_lock lk2(fifo.guard); channel_state->dma_pusher->DispatchCalls();
fifo.pending_work.emplace_back(std::move(entries));
}
}
void Scheduler::ChannelLoop(size_t gpfifo_id, s32 channel_id) {
gpu.BindChannel(channel_id);
auto& fifo = gpfifos[gpfifo_id];
while (true) {
auto* channel_state = channels[channel_id].get();
fifo.guard.lock();
while (!fifo.pending_work.empty()) {
{
fifo.working.store(true, std::memory_order_release);
CommandList&& entries = std::move(fifo.pending_work.front());
channel_state->dma_pusher->Push(std::move(entries));
fifo.pending_work.pop_front();
}
fifo.guard.unlock();
channel_state->dma_pusher->DispatchCalls();
fifo.guard.lock();
}
fifo.working.store(false, std::memory_order_relaxed);
fifo.guard.unlock();
Common::Fiber::YieldTo(fifo.context, *master_control);
gpu.BindChannel(channel_id);
}
} }
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) { void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
s32 channel = new_channel->bind_id; s32 channel = new_channel->bind_id;
std::unique_lock lk(scheduling_guard); std::unique_lock lk(scheduling_guard);
channels.emplace(channel, new_channel); channels.emplace(channel, new_channel);
size_t new_fifo_id;
if (!free_fifos.empty()) {
new_fifo_id = free_fifos.front();
free_fifos.pop_front();
} else {
new_fifo_id = gpfifos.size();
gpfifos.emplace_back();
}
auto& new_fifo = gpfifos[new_fifo_id];
channel_gpfifo_ids[channel] = new_fifo_id;
new_fifo.is_active = true;
new_fifo.bind_id = channel;
new_fifo.pending_work.clear();
std::function<void()> callback = std::bind(&Scheduler::ChannelLoop, this, new_fifo_id, channel);
new_fifo.context = std::make_shared<Common::Fiber>(std::move(callback));
} }
} // namespace Tegra::Control } // namespace Tegra::Control

View File

@ -3,13 +3,10 @@
#pragma once #pragma once
#include <atomic>
#include <deque>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
#include "common/fiber.h"
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
namespace Tegra { namespace Tegra {
@ -25,36 +22,14 @@ public:
explicit Scheduler(GPU& gpu_); explicit Scheduler(GPU& gpu_);
~Scheduler(); ~Scheduler();
void Init();
void Resume();
void Yield();
void Push(s32 channel, CommandList&& entries); void Push(s32 channel, CommandList&& entries);
void DeclareChannel(std::shared_ptr<ChannelState> new_channel); void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
private: private:
void ChannelLoop(size_t gpfifo_id, s32 channel_id);
std::unordered_map<s32, std::shared_ptr<ChannelState>> channels; std::unordered_map<s32, std::shared_ptr<ChannelState>> channels;
std::unordered_map<s32, size_t> channel_gpfifo_ids;
std::mutex scheduling_guard; std::mutex scheduling_guard;
std::shared_ptr<Common::Fiber> master_control;
struct GPFifoContext {
bool is_active;
std::shared_ptr<Common::Fiber> context;
std::deque<CommandList> pending_work;
std::atomic<bool> working{};
std::mutex guard;
s32 bind_id;
};
std::deque<GPFifoContext> gpfifos;
std::deque<size_t> free_fifos;
GPU& gpu; GPU& gpu;
size_t current_fifo_rotation_id{};
GPFifoContext* current_fifo{};
}; };
} // namespace Control } // namespace Control

View File

@ -6,7 +6,6 @@
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "video_core/control/channel_state.h" #include "video_core/control/channel_state.h"
#include "video_core/control/scheduler.h"
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
#include "video_core/engines/fermi_2d.h" #include "video_core/engines/fermi_2d.h"
#include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_compute.h"
@ -15,8 +14,6 @@
#include "video_core/engines/maxwell_dma.h" #include "video_core/engines/maxwell_dma.h"
#include "video_core/engines/puller.h" #include "video_core/engines/puller.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/syncpoint_manager.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
@ -63,14 +60,11 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
} }
void Puller::ProcessFenceActionMethod() { void Puller::ProcessFenceActionMethod() {
auto& syncpoint_manager = gpu.Host1x().GetSyncpointManager();
switch (regs.fence_action.op) { switch (regs.fence_action.op) {
case Puller::FenceOperation::Acquire: case Puller::FenceOperation::Acquire:
while (regs.fence_value > // UNIMPLEMENTED_MSG("Channel Scheduling pending.");
syncpoint_manager.GetGuestSyncpointValue(regs.fence_action.syncpoint_id)) { // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
rasterizer->ReleaseFences(); rasterizer->ReleaseFences();
gpu.Scheduler().Yield();
}
break; break;
case Puller::FenceOperation::Increment: case Puller::FenceOperation::Increment:
rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);

View File

@ -387,14 +387,6 @@ std::shared_ptr<Control::ChannelState> GPU::AllocateChannel() {
return impl->AllocateChannel(); return impl->AllocateChannel();
} }
Tegra::Control::Scheduler& GPU::Scheduler() {
return *impl->scheduler;
}
const Tegra::Control::Scheduler& GPU::Scheduler() const {
return *impl->scheduler;
}
void GPU::InitChannel(Control::ChannelState& to_init) { void GPU::InitChannel(Control::ChannelState& to_init) {
impl->InitChannel(to_init); impl->InitChannel(to_init);
} }

View File

@ -124,8 +124,7 @@ class KeplerCompute;
namespace Control { namespace Control {
struct ChannelState; struct ChannelState;
class Scheduler; }
} // namespace Control
namespace Host1x { namespace Host1x {
class Host1x; class Host1x;
@ -205,12 +204,6 @@ public:
/// Returns a const reference to the shader notifier. /// Returns a const reference to the shader notifier.
[[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const; [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const;
/// Returns GPU Channel Scheduler.
[[nodiscard]] Tegra::Control::Scheduler& Scheduler();
/// Returns GPU Channel Scheduler.
[[nodiscard]] const Tegra::Control::Scheduler& Scheduler() const;
[[nodiscard]] u64 GetTicks() const; [[nodiscard]] u64 GetTicks() const;
[[nodiscard]] bool IsAsync() const; [[nodiscard]] bool IsAsync() const;

View File

@ -34,15 +34,13 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
CommandDataContainer next; CommandDataContainer next;
scheduler.Init();
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
state.queue.PopWait(next, stop_token); state.queue.PopWait(next, stop_token);
if (stop_token.stop_requested()) { if (stop_token.stop_requested()) {
break; break;
} }
if (std::holds_alternative<SubmitListCommand>(next.data)) { if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
scheduler.Resume(); scheduler.Push(submit_list->channel, std::move(submit_list->entries));
} else if (std::holds_alternative<GPUTickCommand>(next.data)) { } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
system.GPU().TickWork(); system.GPU().TickWork();
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
@ -69,16 +67,14 @@ ThreadManager::~ThreadManager() = default;
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context, Core::Frontend::GraphicsContext& context,
Tegra::Control::Scheduler& scheduler_) { Tegra::Control::Scheduler& scheduler) {
rasterizer = renderer.ReadRasterizer(); rasterizer = renderer.ReadRasterizer();
scheduler = &scheduler_;
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context), thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
std::ref(scheduler_), std::ref(state)); std::ref(scheduler), std::ref(state));
} }
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
scheduler->Push(channel, std::move(entries)); PushCommand(SubmitListCommand(channel, std::move(entries)));
PushCommand(SubmitListCommand());
} }
void ThreadManager::FlushRegion(DAddr addr, u64 size) { void ThreadManager::FlushRegion(DAddr addr, u64 size) {

View File

@ -36,7 +36,13 @@ class RendererBase;
namespace VideoCommon::GPUThread { namespace VideoCommon::GPUThread {
/// Command to signal to the GPU thread that a command list is ready for processing /// Command to signal to the GPU thread that a command list is ready for processing
struct SubmitListCommand final {}; struct SubmitListCommand final {
explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_)
: channel{channel_}, entries{std::move(entries_)} {}
s32 channel;
Tegra::CommandList entries;
};
/// Command to signal to the GPU thread to flush a region /// Command to signal to the GPU thread to flush a region
struct FlushRegionCommand final { struct FlushRegionCommand final {
@ -118,7 +124,6 @@ public:
private: private:
/// Pushes a command to be executed by the GPU thread /// Pushes a command to be executed by the GPU thread
u64 PushCommand(CommandData&& command_data, bool block = false); u64 PushCommand(CommandData&& command_data, bool block = false);
Tegra::Control::Scheduler* scheduler;
Core::System& system; Core::System& system;
const bool is_async; const bool is_async;

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h" #include "common/assert.h"
@ -40,7 +40,8 @@ void Decoder::Decode() {
auto frame_copy = frame; auto frame_copy = frame;
if (!frame.get()) { if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode interlaced frame for top 0x{:X} bottom 0x{:X}", LOG_ERROR(HW_GPU,
"Nvdec {} dailed to decode interlaced frame for top 0x{:X} bottom 0x{:X}", id,
luma_top, luma_bottom); luma_top, luma_bottom);
} }
@ -55,7 +56,8 @@ void Decoder::Decode() {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets(); auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
if (!frame.get()) { if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode progressive frame for luma 0x{:X}", luma_offset); LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma 0x{:X}", id,
luma_offset);
} }
if (UsingDecodeOrder()) { if (UsingDecodeOrder()) {

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Ryujinx Team and Contributors // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include <bit> #include <bit>

View File

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Ryujinx Team and Contributors // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -10,7 +10,7 @@ namespace Tegra::Host1x {
Host1x::Host1x(Core::System& system_) Host1x::Host1x(Core::System& system_)
: system{system_}, syncpoint_manager{}, : system{system_}, syncpoint_manager{},
memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 12}, memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 0, 12},
allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {} allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {}
Host1x::~Host1x() = default; Host1x::~Host1x() = default;

View File

@ -66,12 +66,18 @@ public:
void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) { void PushPresentOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto map = m_presentation_order.find(fd); auto map = m_presentation_order.find(fd);
if (map == m_presentation_order.end()) {
return;
}
map->second.emplace_back(offset, std::move(frame)); map->second.emplace_back(offset, std::move(frame));
} }
void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) { void PushDecodeOrder(s32 fd, u64 offset, std::shared_ptr<FFmpeg::Frame>&& frame) {
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto map = m_decode_order.find(fd); auto map = m_decode_order.find(fd);
if (map == m_decode_order.end()) {
return;
}
map->second.insert_or_assign(offset, std::move(frame)); map->second.insert_or_assign(offset, std::move(frame));
} }
@ -82,12 +88,12 @@ public:
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
auto present_map = m_presentation_order.find(fd); auto present_map = m_presentation_order.find(fd);
if (present_map->second.size() > 0) { if (present_map != m_presentation_order.end() && present_map->second.size() > 0) {
return GetPresentOrderLocked(fd); return GetPresentOrderLocked(fd);
} }
auto decode_map = m_decode_order.find(fd); auto decode_map = m_decode_order.find(fd);
if (decode_map->second.size() > 0) { if (decode_map != m_decode_order.end() && decode_map->second.size() > 0) {
return GetDecodeOrderLocked(fd, offset); return GetDecodeOrderLocked(fd, offset);
} }
@ -97,7 +103,7 @@ public:
private: private:
std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) { std::shared_ptr<FFmpeg::Frame> GetPresentOrderLocked(s32 fd) {
auto map = m_presentation_order.find(fd); auto map = m_presentation_order.find(fd);
if (map->second.size() == 0) { if (map == m_presentation_order.end() || map->second.size() == 0) {
return {}; return {};
} }
auto frame = std::move(map->second.front().second); auto frame = std::move(map->second.front().second);
@ -107,6 +113,9 @@ private:
std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) { std::shared_ptr<FFmpeg::Frame> GetDecodeOrderLocked(s32 fd, u64 offset) {
auto map = m_decode_order.find(fd); auto map = m_decode_order.find(fd);
if (map == m_decode_order.end() || map->second.size() == 0) {
return {};
}
auto it = map->second.find(offset); auto it = map->second.find(offset);
if (it == map->second.end()) { if (it == map->second.end()) {
return {}; return {};

View File

@ -24,7 +24,6 @@ Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt, FrameQueue& frame_queue_)
Nvdec::~Nvdec() { Nvdec::~Nvdec() {
LOG_INFO(HW_GPU, "Destroying nvdec {}", id); LOG_INFO(HW_GPU, "Destroying nvdec {}", id);
frame_queue.Close(id);
} }
void Nvdec::ProcessMethod(u32 method, u32 argument) { void Nvdec::ProcessMethod(u32 method, u32 argument) {

View File

@ -100,10 +100,11 @@ Vic::Vic(Host1x& host1x_, s32 id_, u32 syncpt, FrameQueue& frame_queue_)
Vic::~Vic() { Vic::~Vic() {
LOG_INFO(HW_GPU, "Destroying vic {}", id); LOG_INFO(HW_GPU, "Destroying vic {}", id);
frame_queue.Close(id);
} }
void Vic::ProcessMethod(u32 method, u32 arg) { void Vic::ProcessMethod(u32 method, u32 arg) {
LOG_TRACE(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method)); LOG_TRACE(HW_GPU, "Vic {} method 0x{:X}", id, static_cast<u32>(method));
regs.reg_array[method] = arg; regs.reg_array[method] = arg;
switch (static_cast<Method>(method * sizeof(u32))) { switch (static_cast<Method>(method * sizeof(u32))) {
@ -140,7 +141,7 @@ void Vic::Execute() {
auto frame = frame_queue.GetFrame(nvdec_id, luma_offset); auto frame = frame_queue.GetFrame(nvdec_id, luma_offset);
if (!frame.get()) { if (!frame.get()) {
LOG_ERROR(HW_GPU, "Vic failed to get frame with offset 0x{:X}", luma_offset); LOG_ERROR(HW_GPU, "Vic {} failed to get frame with offset 0x{:X}", id, luma_offset);
continue; continue;
} }

View File

@ -22,11 +22,12 @@ using Tegra::Memory::GuestMemoryFlags;
std::atomic<size_t> MemoryManager::unique_identifier_generator{}; std::atomic<size_t> MemoryManager::unique_identifier_generator{};
MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_, MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_, u64 big_page_bits_, u64 page_bits_) u64 address_space_bits_, GPUVAddr split_address_, u64 big_page_bits_,
u64 page_bits_)
: system{system_}, memory{memory_}, address_space_bits{address_space_bits_}, : system{system_}, memory{memory_}, address_space_bits{address_space_bits_},
page_bits{page_bits_}, big_page_bits{big_page_bits_}, entries{}, big_entries{}, split_address{split_address_}, page_bits{page_bits_}, big_page_bits{big_page_bits_},
page_table{address_space_bits, address_space_bits + page_bits - 38, entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38,
page_bits != big_page_bits ? page_bits : 0}, page_bits != big_page_bits ? page_bits : 0},
kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add( kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add(
1, std::memory_order_acq_rel)}, 1, std::memory_order_acq_rel)},
accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} { accumulator{std::make_unique<VideoCommon::InvalidationAccumulator>()} {
@ -48,10 +49,10 @@ MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager&
entries.resize(page_table_size / 32, 0); entries.resize(page_table_size / 32, 0);
} }
MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 big_page_bits_, MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_,
u64 page_bits_) GPUVAddr split_address_, u64 big_page_bits_, u64 page_bits_)
: MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, big_page_bits_, : MemoryManager(system_, system_.Host1x().MemoryManager(), address_space_bits_, split_address_,
page_bits_) {} big_page_bits_, page_bits_) {}
MemoryManager::~MemoryManager() = default; MemoryManager::~MemoryManager() = default;

View File

@ -36,10 +36,11 @@ namespace Tegra {
class MemoryManager final { class MemoryManager final {
public: public:
explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40, explicit MemoryManager(Core::System& system_, u64 address_space_bits_ = 40,
u64 big_page_bits_ = 16, u64 page_bits_ = 12); GPUVAddr split_address = 1ULL << 34, u64 big_page_bits_ = 16,
explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_ = 40, u64 big_page_bits_ = 16,
u64 page_bits_ = 12); u64 page_bits_ = 12);
explicit MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& memory_,
u64 address_space_bits_ = 40, GPUVAddr split_address = 1ULL << 34,
u64 big_page_bits_ = 16, u64 page_bits_ = 12);
~MemoryManager(); ~MemoryManager();
static constexpr bool HAS_FLUSH_INVALIDATION = true; static constexpr bool HAS_FLUSH_INVALIDATION = true;
@ -194,6 +195,7 @@ private:
MaxwellDeviceMemoryManager& memory; MaxwellDeviceMemoryManager& memory;
const u64 address_space_bits; const u64 address_space_bits;
GPUVAddr split_address;
const u64 page_bits; const u64 page_bits;
u64 address_space_size; u64 address_space_size;
u64 page_size; u64 page_size;

View File

@ -32,9 +32,14 @@ struct Client::Impl {
Impl(std::string host_, std::string username_, std::string token_) Impl(std::string host_, std::string username_, std::string token_)
: host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} { : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {
std::scoped_lock lock{jwt_cache.mutex}; std::scoped_lock lock{jwt_cache.mutex};
if (username == jwt_cache.username && token == jwt_cache.token) { if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
jwt = jwt_cache.jwt; jwt = jwt_cache.jwt;
} }
// Normalize host expression
if (!this->host.empty() && this->host.back() == '/') {
static_cast<void>(this->host.pop_back());
}
} }
/// A generic function handles POST, GET and DELETE request together /// A generic function handles POST, GET and DELETE request together
@ -71,18 +76,16 @@ struct Client::Impl {
const std::string& jwt_ = "", const std::string& username_ = "", const std::string& jwt_ = "", const std::string& username_ = "",
const std::string& token_ = "") { const std::string& token_ = "") {
if (cli == nullptr) { if (cli == nullptr) {
cli = std::make_unique<httplib::Client>(host); cli = std::make_unique<httplib::Client>(host.c_str());
cli->set_connection_timeout(TIMEOUT_SECONDS);
cli->set_read_timeout(TIMEOUT_SECONDS);
cli->set_write_timeout(TIMEOUT_SECONDS);
} }
if (!cli->is_valid()) { if (!cli->is_valid()) {
LOG_ERROR(WebService, "Client is invalid, skipping request!"); LOG_ERROR(WebService, "Invalid URL {}", host + path);
return {}; return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
} }
cli->set_connection_timeout(TIMEOUT_SECONDS);
cli->set_read_timeout(TIMEOUT_SECONDS);
cli->set_write_timeout(TIMEOUT_SECONDS);
httplib::Headers params; httplib::Headers params;
if (!jwt_.empty()) { if (!jwt_.empty()) {
params = { params = {
@ -107,15 +110,15 @@ struct Client::Impl {
request.headers = params; request.headers = params;
request.body = data; request.body = data;
httplib::Response response; httplib::Result result = cli->send(request);
httplib::Error error;
if (!cli->send(request, response, error)) { if (!result) {
LOG_ERROR(WebService, "{} to {} returned null (httplib Error: {})", method, host + path, LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
httplib::to_string(error));
return WebResult{WebResult::Code::LibError, "Null response", ""}; return WebResult{WebResult::Code::LibError, "Null response", ""};
} }
httplib::Response response = result.value();
if (response.status >= 400) { if (response.status >= 400) {
LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
response.status); response.status);

View File

@ -45,15 +45,23 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); connect(timeout_timer.get(), &QTimer::timeout, [this] {
const bool is_button_pressed = pressed_buttons != Core::HID::NpadButton::None ||
pressed_home_button || pressed_capture_button;
SetPollingResult(!is_button_pressed);
});
connect(poll_timer.get(), &QTimer::timeout, [this] { connect(poll_timer.get(), &QTimer::timeout, [this] {
const auto buttons = controller->GetNpadButtons(); pressed_buttons |= controller->GetNpadButtons().raw;
const auto home_pressed = controller->GetHomeButtons().home != 0; pressed_home_button |= this->controller->GetHomeButtons().home != 0;
const auto capture_pressed = controller->GetCaptureButtons().capture != 0; pressed_capture_button |= this->controller->GetCaptureButtons().capture != 0;
if (home_pressed || capture_pressed) { if (pressed_buttons != Core::HID::NpadButton::None || pressed_home_button ||
SetPollingResult(buttons.raw, false); pressed_capture_button) {
return; const QString button_name =
GetButtonCombinationName(pressed_buttons, pressed_home_button,
pressed_capture_button) +
QStringLiteral("...");
model->setData(button_model_index, button_name);
} }
}); });
RetranslateUI(); RetranslateUI();
@ -154,16 +162,14 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
const auto previous_key = model->data(index); const auto previous_key = model->data(index);
input_setter = [this, index, previous_key](const Core::HID::NpadButton button, input_setter = [this, index, previous_key](const bool cancel) {
const bool cancel) {
if (cancel) { if (cancel) {
model->setData(index, previous_key); model->setData(index, previous_key);
return; return;
} }
const auto home_pressed = this->controller->GetHomeButtons().home != 0;
const auto capture_pressed = this->controller->GetCaptureButtons().capture != 0;
const QString button_string = const QString button_string =
GetButtonCombinationName(button, home_pressed, capture_pressed); GetButtonCombinationName(pressed_buttons, pressed_home_button, pressed_capture_button);
const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string); const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string);
@ -177,17 +183,22 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
} }
}; };
button_model_index = index;
pressed_buttons = Core::HID::NpadButton::None;
pressed_home_button = false;
pressed_capture_button = false;
model->setData(index, tr("[waiting]")); model->setData(index, tr("[waiting]"));
timeout_timer->start(2500); // Cancel after 2.5 seconds timeout_timer->start(2500); // Cancel after 2.5 seconds
poll_timer->start(200); // Check for new inputs every 200ms poll_timer->start(100); // Check for new inputs every 100ms
// We need to disable configuration to be able to read npad buttons // We need to disable configuration to be able to read npad buttons
controller->DisableConfiguration(); controller->DisableConfiguration();
} }
void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) { void ConfigureHotkeys::SetPollingResult(const bool cancel) {
timeout_timer->stop(); timeout_timer->stop();
poll_timer->stop(); poll_timer->stop();
(*input_setter)(button, cancel); (*input_setter)(cancel);
// Re-Enable configuration // Re-Enable configuration
controller->EnableConfiguration(); controller->EnableConfiguration();

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QStandardItemModel>
#include <QWidget> #include <QWidget>
namespace Common { namespace Common {
@ -54,14 +55,20 @@ private:
void RestoreControllerHotkey(QModelIndex index); void RestoreControllerHotkey(QModelIndex index);
void RestoreHotkey(QModelIndex index); void RestoreHotkey(QModelIndex index);
void SetPollingResult(bool cancel);
QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const;
std::unique_ptr<Ui::ConfigureHotkeys> ui; std::unique_ptr<Ui::ConfigureHotkeys> ui;
QStandardItemModel* model; QStandardItemModel* model;
void SetPollingResult(Core::HID::NpadButton button, bool cancel); bool pressed_home_button;
QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const; bool pressed_capture_button;
QModelIndex button_model_index;
Core::HID::NpadButton pressed_buttons;
Core::HID::EmulatedController* controller; Core::HID::EmulatedController* controller;
std::unique_ptr<QTimer> timeout_timer; std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer; std::unique_ptr<QTimer> poll_timer;
std::optional<std::function<void(Core::HID::NpadButton, bool)>> input_setter; std::optional<std::function<void(bool)>> input_setter;
}; };

View File

@ -73,8 +73,11 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles"));
// Only show Linux tab on Unix // Only show Linux tab on Unix
linux_tab->setVisible(false);
#ifdef __unix__ #ifdef __unix__
linux_tab->setVisible(true);
ui->tabWidget->addTab(linux_tab.get(), tr("Linux")); ui->tabWidget->addTab(linux_tab.get(), tr("Linux"));
#endif #endif

View File

@ -248,82 +248,22 @@ void ConfigureUi::RetranslateUI() {
} }
void ConfigureUi::InitializeLanguageComboBox() { void ConfigureUi::InitializeLanguageComboBox() {
// This is a list of lexicographically sorted languages, only the available translations are
// shown to the user.
static const struct {
const QString name;
const char* id;
} languages[] = {
// clang-format off
{QStringLiteral(u"Bahasa Indonesia"), "id"}, // Indonesian
{QStringLiteral(u"Bahasa Melayu"), "ms"}, // Malay
{QStringLiteral(u"Catal\u00E0"), "ca"}, // Catalan
{QStringLiteral(u"\u010Ce\u0161tina"), "cs"}, // Czech
{QStringLiteral(u"Dansk"), "da"}, // Danish
{QStringLiteral(u"Deutsch"), "de"}, // German
{QStringLiteral(u"English"), "en"}, // English
{QStringLiteral(u"Espa\u00F1ol"), "es"}, // Spanish
{QStringLiteral(u"Fran\u00E7ais"), "fr"}, // French
{QStringLiteral(u"Hrvatski"), "hr"}, // Croatian
{QStringLiteral(u"Italiano"), "it"}, // Italian
{QStringLiteral(u"Magyar"), "hu"}, // Hungarian
{QStringLiteral(u"Nederlands"), "nl"}, // Dutch
{QStringLiteral(u"Norsk bokm\u00E5l"), "nb"}, // Norwegian
{QStringLiteral(u"Polski"), "pl"}, // Polish
{QStringLiteral(u"Portugu\u00EAs"), "pt_PT"}, // Portuguese
{QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_BR"}, // Portuguese (Brazil)
{QStringLiteral(u"Rom\u00E2n\u0103"), "ro"}, // Romanian
{QStringLiteral(u"Srpski"), "sr"}, // Serbian
{QStringLiteral(u"Suomi"), "fi"}, // Finnish
{QStringLiteral(u"Svenska"), "sv"}, // Swedish
{QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t"), "vi"}, // Vietnamese
{QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t (Vi\u1EC7t Nam)"), "vi_VN"}, // Vietnamese
{QStringLiteral(u"T\u00FCrk\u00E7e"), "tr_TR"}, // Turkish
{QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"}, // Greek
{QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"), "ru_RU"}, // Russian
{QStringLiteral(u"\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"),
"uk"}, // Ukrainian
{QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"), "ar"}, // Arabic
{QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), "fa"}, // Farsi
{QStringLiteral(u"\uD55C\uAD6D\uC5B4"), "ko_KR"}, // Korean
{QStringLiteral(u"\u65E5\u672C\u8A9E"), "ja_JP"}, // Japanese
{QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"), "zh_CN"}, // Simplified Chinese
{QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"), "zh_TW"}, // Traditional Chinese
// clang-format on
};
ui->language_combobox->addItem(tr("<System>"), QString{}); ui->language_combobox->addItem(tr("<System>"), QString{});
QDir languages_dir{QStringLiteral(":/languages")}; ui->language_combobox->addItem(tr("English"), QStringLiteral("en"));
QStringList language_files = languages_dir.entryList(); QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags);
for (const auto& lang : languages) { while (it.hasNext()) {
if (QString::fromLatin1(lang.id) == QStringLiteral("en")) { QString locale = it.next();
ui->language_combobox->addItem(lang.name, QStringLiteral("en"));
language_files.removeOne(QStringLiteral("en.qm"));
continue;
}
for (int i = 0; i < language_files.size(); ++i) {
QString locale = language_files[i];
locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
if (QString::fromLatin1(lang.id) == locale) {
ui->language_combobox->addItem(lang.name, locale);
language_files.removeAt(i);
break;
}
}
}
// Anything remaining will be at the bottom
for (const QString& file : language_files) {
LOG_CRITICAL(Frontend, "Unexpected Language File: {}", file.toStdString());
QString locale = file;
locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
const QString language_name = QLocale::languageToString(QLocale(locale).language()); locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1);
const QString lang = QStringLiteral("%1 [%2]").arg(language_name, locale); const QString lang = QLocale::languageToString(QLocale(locale).language());
ui->language_combobox->addItem(lang, locale); const QString country = QLocale::countryToString(QLocale(locale).country());
ui->language_combobox->addItem(QStringLiteral("%1 (%2)").arg(lang, country), locale);
} }
// Unlike other configuration changes, interface language changes need to be reflected on the // Unlike other configuration changes, interface language changes need to be reflected on the
// interface immediately. This is done by passing a signal to the main window, and then // interface immediately. This is done by passing a signal to the main window, and then
// retranslating when passing back. // retranslating when passing back.
connect(ui->language_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(ui->language_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureUi::OnLanguageChanged); &ConfigureUi::OnLanguageChanged);
} }

View File

@ -193,8 +193,7 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType
if (!Settings::values.controller_navigation) { if (!Settings::values.controller_navigation) {
return; return;
} }
if (button_sequence.npad.raw == Core::HID::NpadButton::None && if (button_sequence.npad.raw == Core::HID::NpadButton::None) {
button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {
return; return;
} }