service/ldn: Implement SetProtocol and SetWirelessAudioPolicy commands

Adds support for LDN service commands introduced in firmware 20.0.0+:
- Command 105: SetWirelessAudioPolicy (stubbed)
- Command 106: SetProtocol

This enables ACNH Update 3.0 and other games using newer SDK versions
to properly initialize LDN for local multiplayer functionality.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2026-01-19 16:29:46 +10:00
parent 23bb5078a7
commit c720e354d2
5 changed files with 94 additions and 2 deletions

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -128,6 +129,21 @@ enum class WirelessControllerRestriction : u32 {
Default,
};
enum class WirelessAudioRestriction : u32 {
Disabled,
Enabled,
};
// Protocol enum for SetProtocol command (18.0.0+)
// On NX, permission bitmask is 0xA (allows Protocol 1 and 3 only)
enum class Protocol : u32 {
Default = 0,
NX = 1,
// S2 = 2, // Switch 2 only
NXAndOunce = 3,
// S2_Alt = 4, // Switch 2 only
};
struct ConnectOption {
union {
u32 raw;

View File

@@ -22,6 +22,8 @@ ISystemLocalCommunicationService::ISystemLocalCommunicationService(Core::System&
{102, D<&ISystemLocalCommunicationService::Scan>, "Scan"},
{103, D<&ISystemLocalCommunicationService::ScanPrivate>, "ScanPrivate"},
{104, D<&ISystemLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"},
{105, D<&ISystemLocalCommunicationService::SetWirelessAudioPolicy>, "SetWirelessAudioPolicy"},
{106, D<&ISystemLocalCommunicationService::SetProtocol>, "SetProtocol"},
{200, D<&ISystemLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"},
{201, D<&ISystemLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"},
{202, D<&ISystemLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
@@ -116,7 +118,38 @@ Result ISystemLocalCommunicationService::ScanPrivate(Out<s16> network_count, Wif
}
Result ISystemLocalCommunicationService::SetWirelessControllerRestriction(WirelessControllerRestriction wireless_restriction) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
LOG_WARNING(Service_LDN, "(STUBBED) called, wireless_restriction={}",
static_cast<u32>(wireless_restriction));
R_SUCCEED();
}
Result ISystemLocalCommunicationService::SetWirelessAudioPolicy(WirelessAudioRestriction wireless_audio_restriction) {
LOG_WARNING(Service_LDN, "(STUBBED) called, wireless_audio_restriction={}",
static_cast<u32>(wireless_audio_restriction));
R_SUCCEED();
}
Result ISystemLocalCommunicationService::SetProtocol(Protocol protocol) {
LOG_INFO(Service_LDN, "called, protocol={}", static_cast<u32>(protocol));
// On NX, the protocol permission bitmask is 0xA (allows 1 and 3)
// The SDK passes value 1 for Protocol 0/1, and 3 is passed directly if specified
// Input must be non-zero, and BIT(input) must be set in permission bitmask
const u32 protocol_value = static_cast<u32>(protocol);
// For NX compatibility, we accept protocols 1 (NX) and 3 (NXAndOunce)
// Protocol 0 (Default) is typically converted to 1 by the SDK before calling
if (protocol_value == 0) {
// Default is treated as NX
current_protocol = Protocol::NX;
} else if (protocol_value == 1 || protocol_value == 3) {
current_protocol = protocol;
} else {
// Invalid protocol for NX - but we'll accept it as a stub
LOG_WARNING(Service_LDN, "Invalid protocol value {} for NX, accepting anyway", protocol_value);
current_protocol = protocol;
}
R_SUCCEED();
}

View File

@@ -39,6 +39,8 @@ private:
Result ScanPrivate(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info);
Result SetWirelessControllerRestriction(WirelessControllerRestriction wireless_restriction);
Result SetWirelessAudioPolicy(WirelessAudioRestriction wireless_audio_restriction);
Result SetProtocol(Protocol protocol);
Result OpenAccessPoint();
Result CloseAccessPoint();
Result CreateNetwork(const CreateNetworkConfig& create_config);
@@ -61,6 +63,8 @@ private:
Result FinalizeSystem();
Result SetOperationMode(u32 mode);
Result InitializeSystem2();
Protocol current_protocol{Protocol::NX};
};
} // namespace Service::LDN

View File

@@ -37,6 +37,8 @@ IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& sys
{102, D<&IUserLocalCommunicationService::Scan>, "Scan"},
{103, D<&IUserLocalCommunicationService::ScanPrivate>, "ScanPrivate"},
{104, D<&IUserLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"},
{105, D<&IUserLocalCommunicationService::SetWirelessAudioPolicy>, "SetWirelessAudioPolicy"},
{106, D<&IUserLocalCommunicationService::SetProtocol>, "SetProtocol"},
{200, D<&IUserLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"},
{201, D<&IUserLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"},
{202, D<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
@@ -189,7 +191,39 @@ Result IUserLocalCommunicationService::ScanPrivate(
Result IUserLocalCommunicationService::SetWirelessControllerRestriction(
WirelessControllerRestriction wireless_restriction) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
LOG_WARNING(Service_LDN, "(STUBBED) called, wireless_restriction={}",
static_cast<u32>(wireless_restriction));
R_SUCCEED();
}
Result IUserLocalCommunicationService::SetWirelessAudioPolicy(
WirelessAudioRestriction wireless_audio_restriction) {
LOG_WARNING(Service_LDN, "(STUBBED) called, wireless_audio_restriction={}",
static_cast<u32>(wireless_audio_restriction));
R_SUCCEED();
}
Result IUserLocalCommunicationService::SetProtocol(Protocol protocol) {
LOG_INFO(Service_LDN, "called, protocol={}", static_cast<u32>(protocol));
// On NX, the protocol permission bitmask is 0xA (allows 1 and 3)
// The SDK passes value 1 for Protocol 0/1, and 3 is passed directly if specified
// Input must be non-zero, and BIT(input) must be set in permission bitmask
const u32 protocol_value = static_cast<u32>(protocol);
// For NX compatibility, we accept protocols 1 (NX) and 3 (NXAndOunce)
// Protocol 0 (Default) is typically converted to 1 by the SDK before calling
if (protocol_value == 0) {
// Default is treated as NX
current_protocol = Protocol::NX;
} else if (protocol_value == 1 || protocol_value == 3) {
current_protocol = protocol;
} else {
// Invalid protocol for NX - but we'll accept it as a stub
LOG_WARNING(Service_LDN, "Invalid protocol value {} for NX, accepting anyway", protocol_value);
current_protocol = protocol;
}
R_SUCCEED();
}

View File

@@ -53,6 +53,10 @@ private:
Result SetWirelessControllerRestriction(WirelessControllerRestriction wireless_restriction);
Result SetWirelessAudioPolicy(WirelessAudioRestriction wireless_audio_restriction);
Result SetProtocol(Protocol protocol);
Result OpenAccessPoint();
Result CloseAccessPoint();
@@ -106,6 +110,7 @@ private:
Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
bool is_initialized{};
Protocol current_protocol{Protocol::NX};
};
} // namespace Service::LDN