diff --git a/dm/host/hle_hid.c b/dm/host/hle_hid.c index cc4dcf62be..547c94a9a2 100644 --- a/dm/host/hle_hid.c +++ b/dm/host/hle_hid.c @@ -892,6 +892,21 @@ HLE_FORCE("_ZN2nn3hid13GetJcmsStatesEPNS0_9JcmsStateEiRKNS0_10JcmsHandleE", "Joy // hid::detail::GetJcmsState): with no sensor, a cleared state (sampling number 0, no movement, attributes 0: not connected) HLE_FORCE("_ZN2nn3hid12GetJcmsStateEPNS0_9JcmsStateERKNS0_10JcmsHandleE", "Joy-Con mouse sensor state: the newest (cleared until a front end reports the sensor)", void, h_hid_JcmsState, void *out, const uint32_t *h) { if (out) { memset(out, 0, 32); if (h) jcms_copy(out, 1, *h); } } +// A Switch 2 SDK's guard against a Home press made by accident on a controller (Drag x Drive turns it on for player 1's Joy-Con 2 as they connect, main+0x2c80c4). The SDK's own +// opens the hid service, which the host does not serve, and calls through the null session (sdk+0x63ce10). It sets a flag in hid and returns nothing; the host handles Home itself +// (home_quit.h), so the setting is logged +static _Atomic uint16_t g_home_protect; // the setting per npad (No 1-8, then Other, then Handheld), as the game left it; off until it turns it on +static int home_protect_bit(const uint32_t *id) { return !id ? -1 : *id < 8 ? (int)*id : *id == 0x10 ? 8 : *id == 0x20 ? 9 : -1; } +HLE_FORCE("_ZN2nn3hid41EnableUnintendedHomeButtonInputProtectionERKjb", "Home button protection: recorded and logged, the host handles Home itself", void, h_hid_HomeProtect, + const uint32_t *id, bool on) { + int b = home_protect_bit(id); + if (b >= 0) { if (on) atomic_fetch_or(&g_home_protect, (uint16_t)(1u << b)); else atomic_fetch_and(&g_home_protect, (uint16_t)~(1u << b)); } + LOG("hid: EnableUnintendedHomeButtonInputProtection npad %#x %s", id ? *id : 0xffffffffu, on ? "on" : "off"); +} +// The question (Nintendo Switch Sports Resort asks it at boot): the SDK's own asks the hid service, which the host does not serve, and aborted on its error (Result 0x58cca at +// sdk+0x6ca40c). The answer is what the game set, off by default +HLE_FORCE("_ZN2nn3hid44IsUnintendedHomeButtonInputProtectionEnabledERKj", "Home button protection: what the game set (off by default)", bool, h_hid_HomeProtectOn, + const uint32_t *id) { int b = home_protect_bit(id); return b >= 0 && (atomic_load(&g_home_protect) >> b) & 1; } // The system setting for the mouse pointer's speed (System Settings, Mouse): the default, 1 HLE_FORCE("_ZN2nn3hid25GetMousePointerSpeedScaleEv", "mouse pointer speed setting: the default, 1.0", float, h_hid_MouseSpeed, void) { return 1.0f; } // The Switch 2's pointer on top of those sensors (Nmouse; Fire Emblem Fortune's Weave initializes it at boot with the 1920x1080 screen it maps the pointer to, main+0x650a24). InitializeNmouse opens the diff --git a/dm/host/tests/t_hid.c b/dm/host/tests/t_hid.c index 58ab42e6cf..201d7f825e 100644 --- a/dm/host/tests/t_hid.c +++ b/dm/host/tests/t_hid.c @@ -104,6 +104,16 @@ static void t_npads(void) { CHECK(buttons_of(0, STYLE_FULLKEY) == DM_BTN_Y && lx_of(0) == 0, "player 1 does not see player 2"); CHECK(buttons_of(1, STYLE_HANDHELD) == 0, "player 2 is not a handheld controller"); CHECK(h_hid_Led(&(uint32_t){0}) == 0x1 && h_hid_Led(&(uint32_t){1}) == 0x3 && h_hid_Led(&(uint32_t){7}) == 0x6, "player LED patterns"); + { // Home button protection: what the game set, per npad, off by default (Nintendo Switch Sports Resort asks at boot) + uint32_t no1 = 0, no8 = 7, other = 0x10, handheld = 0x20, bad = 9; + CHECK(!h_hid_HomeProtectOn(&no1) && !h_hid_HomeProtectOn(&handheld), "Home protection starts off"); + h_hid_HomeProtect(&no1, true); h_hid_HomeProtect(&handheld, true); + CHECK(h_hid_HomeProtectOn(&no1) && h_hid_HomeProtectOn(&handheld) && !h_hid_HomeProtectOn(&no8) && !h_hid_HomeProtectOn(&other), "on for the npads the game turned it on for"); + h_hid_HomeProtect(&no1, false); + CHECK(!h_hid_HomeProtectOn(&no1) && h_hid_HomeProtectOn(&handheld), "off again for No1, Handheld kept"); + h_hid_HomeProtect(&bad, true); CHECK(!h_hid_HomeProtectOn(&bad) && !h_hid_HomeProtectOn(NULL), "an id that is no npad: off"); + h_hid_HomeProtect(&handheld, false); + } int32_t info[2]; int8_t arg[8] = {1, 4}; CHECK(h_hid_ShowCtrl(info, arg) == RES_OK && info[0] == 2 && info[1] == 0, "the controller applet reports two players, player 1 selected"); CHECK(applet_opens == 1 && applet_depth == 0 && applet_closes_at_depth0 == 1, "the controller applet opens and closes once around its answer (focus 2, then 1)"); diff --git a/docs/agents/symptoms.md b/docs/agents/symptoms.md index c9468cde5d..3f5d19b236 100644 --- a/docs/agents/symptoms.md +++ b/docs/agents/symptoms.md @@ -348,6 +348,7 @@ One line per symptom in the [subsystem references](../reference/): what you see - an abort connecting to `pl:u` at boot; a failed `nn::audio` call at boot. → [Built-in fonts and the audio device notification of SDK 21](../reference/services.md#built-in-fonts-and-the-audio-device-notification-of-sdk-21) - an abort in `nn::hid` at boot, in a Switch 2 game that sets up the Joy-Con's mouse mode. → [The Joy-Con mouse pointer](../reference/services.md#the-joy-con-mouse-pointer) - a Switch 2 game's mouse mode does nothing with Joy-Con 2s: its pointer stays still, or a game that offers its mouse modes only to Joy-Con 2 controllers finds none. → [The Joy-Con 2 mouse sensor and pointer](../reference/services.md#the-joy-con-2-mouse-sensor-and-pointer) +- a Switch 2 game crashes as a Joy-Con 2 joins: `[RED] sf: the game connects to service hid itself, which the host does not serve`, then `CRASH sig=11 addr=0` at `sdk+0x63ce10`, called from `nn::hid::EnableUnintendedHomeButtonInputProtection`. → [Home button protection on a Switch 2 controller](../reference/services.md#home-button-protection-on-a-switch-2-controller) - a crash at boot in `nn::vi` under `nn::streamplay::CreateIndividualDisplays`. → [GameShare guest displays](../reference/services.md#gameshare-guest-displays) - `[RED] sf: the game connects to service chat:u itself, which the host does not serve`, then `game called nn::diag abort: Result 0xe15` under `nn::chat::Initialize`. → [GameChat at boot](../reference/services.md#gamechat-at-boot) - `CRASH sig=11 addr=0 pc=sdk+0x6be4d4` in `nn::chat::GetChattingUid()`, from the game's title screen code. → [GameChat queries from the title screen (SDK 21)](../reference/services.md#gamechat-queries-from-the-title-screen-sdk-21) diff --git a/docs/reference/services.md b/docs/reference/services.md index 1ed0b861fe..f0876b2160 100644 --- a/docs/reference/services.md +++ b/docs/reference/services.md @@ -1100,6 +1100,14 @@ Most service calls can be found before they crash, before the first boot or the - `DM_JCMS_LOG=1` logs the game's sensor and pointer calls, at most once a second each kind. - **Found on:** Nintendo Switch 2 Welcome Tour (040057C02159A000; its note and profile are their own pull request): its cursor and its mouse exhibits, with a left and a right Joy-Con 2. `t_hid` checks the states, the handles and their order, and the styles. +### Home button protection on a Switch 2 controller + +- **Symptom:** a Switch 2 game crashes as a Joy-Con 2 joins: `[RED] sf: the game connects to service hid itself, which the host does not serve`, then `CRASH sig=11 addr=0` at `sdk+0x63ce10`, called from `nn::hid::EnableUnintendedHomeButtonInputProtection`. +- **Cause:** `nn::hid::EnableUnintendedHomeButtonInputProtection(const NpadIdType&, bool)` turns on a guard against a Home press made by accident on that controller. It returns nothing; the SDK's own sets the flag through the hid service, which the host does not serve, and calls through the null session. +- **Fix:** the host answers it (`hle_hid.c`), records the setting per npad (No 1-8, Other, Handheld) and logs it (`hid: EnableUnintendedHomeButtonInputProtection npad 0 on`). The question, `IsUnintendedHomeButtonInputProtectionEnabled(const NpadIdType&)`, whose SDK function asks the service too, answers what the game set, off by default. The host handles the Home button itself ([home_quit.h](../../dm/host/home_quit.h)), so nothing else changes. +- **Found on:** Drag x Drive (040041601D83A000, v196608, SDK 20; no profile in the repository yet): 43 s in, as player 1's Joy-Con 2 pair joined (`main+0x2c80c4`). Nintendo Switch Sports Resort (`04008E001F098000`, v65536; no profile yet) asks the question at boot: the SDK's own aborted on its error (`Result 0x58cca` at `sdk+0x6ca40c`). +- **Check:** `make -C dm/host/tests GAME= run` (`t_hid`). + ### GameShare guest displays - **Symptom:** a crash at boot in `nn::vi` under `nn::streamplay::CreateIndividualDisplays`.