summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/hle/service/ldn/errors.h12
-rw-r--r--src/core/hle/service/ldn/ldn.cpp431
-rw-r--r--src/core/hle/service/ldn/ldn.h73
-rw-r--r--src/core/hle/service/ldn/ldn_results.h28
-rw-r--r--src/core/hle/service/ldn/ldn_types.h298
6 files changed, 773 insertions, 72 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9dbe5bdca..d314ff1ce 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -504,9 +504,10 @@ add_library(core STATIC
hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
- hle/service/ldn/errors.h
+ hle/service/ldn/ldn_results.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
+ hle/service/ldn/ldn_types.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h
deleted file mode 100644
index 972a74806..000000000
--- a/src/core/hle/service/ldn/errors.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/result.h"
-
-namespace Service::LDN {
-
-constexpr Result ERROR_DISABLED{ErrorModule::LDN, 22};
-
-} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 125d4dc4c..c8e1898f4 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -3,11 +3,12 @@
#include <memory>
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/result.h"
-#include "core/hle/service/ldn/errors.h"
+#include "core/core.h"
#include "core/hle/service/ldn/ldn.h"
-#include "core/hle/service/sm/sm.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+
+#undef CreateEvent
namespace Service::LDN {
@@ -96,81 +97,393 @@ public:
}
};
-class IUserLocalCommunicationService final
- : public ServiceFramework<IUserLocalCommunicationService> {
-public:
- explicit IUserLocalCommunicationService(Core::System& system_)
- : ServiceFramework{system_, "IUserLocalCommunicationService"} {
- // clang-format off
+IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& system_)
+ : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
+ service_context{system, "IUserLocalCommunicationService"}, room_network{
+ system_.GetRoomNetwork()} {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IUserLocalCommunicationService::GetState, "GetState"},
- {1, nullptr, "GetNetworkInfo"},
+ {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
{2, nullptr, "GetIpv4Address"},
- {3, nullptr, "GetDisconnectReason"},
- {4, nullptr, "GetSecurityParameter"},
- {5, nullptr, "GetNetworkConfig"},
- {100, nullptr, "AttachStateChangeEvent"},
- {101, nullptr, "GetNetworkInfoLatestUpdate"},
- {102, nullptr, "Scan"},
- {103, nullptr, "ScanPrivate"},
+ {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
+ {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
+ {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
+ {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
+ {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
+ {102, &IUserLocalCommunicationService::Scan, "Scan"},
+ {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
{104, nullptr, "SetWirelessControllerRestriction"},
- {200, nullptr, "OpenAccessPoint"},
- {201, nullptr, "CloseAccessPoint"},
- {202, nullptr, "CreateNetwork"},
- {203, nullptr, "CreateNetworkPrivate"},
- {204, nullptr, "DestroyNetwork"},
+ {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
+ {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
+ {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
+ {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
+ {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
{205, nullptr, "Reject"},
- {206, nullptr, "SetAdvertiseData"},
- {207, nullptr, "SetStationAcceptPolicy"},
- {208, nullptr, "AddAcceptFilterEntry"},
+ {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
+ {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
+ {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
{209, nullptr, "ClearAcceptFilter"},
- {300, nullptr, "OpenStation"},
- {301, nullptr, "CloseStation"},
- {302, nullptr, "Connect"},
+ {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
+ {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
+ {302, &IUserLocalCommunicationService::Connect, "Connect"},
{303, nullptr, "ConnectPrivate"},
- {304, nullptr, "Disconnect"},
- {400, nullptr, "Initialize"},
- {401, nullptr, "Finalize"},
- {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+
+ {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
+ {400, &IUserLocalCommunicationService::Initialize, "Initialize"},
+ {401, &IUserLocalCommunicationService::Finalize, "Finalize"},
+ {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
};
- // clang-format on
+ // clang-format on
- RegisterHandlers(functions);
+ RegisterHandlers(functions);
+
+ state_change_event =
+ service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
+}
+
+IUserLocalCommunicationService::~IUserLocalCommunicationService() {
+ service_context.CloseEvent(state_change_event);
+}
+
+void IUserLocalCommunicationService::OnEventFired() {
+ state_change_event->GetWritableEvent().Signal();
+}
+
+void IUserLocalCommunicationService::GetState(Kernel::HLERequestContext& ctx) {
+ State state = State::Error;
+ LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(state);
+}
+
+void IUserLocalCommunicationService::GetNetworkInfo(Kernel::HLERequestContext& ctx) {
+ const auto write_buffer_size = ctx.GetWriteBufferSize();
+
+ if (write_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
}
- void GetState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
+ NetworkInfo networkInfo{};
+ const auto rc = ResultSuccess;
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
- IPC::ResponseBuilder rb{ctx, 3};
+ LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
+ networkInfo.common.ssid.GetStringValue(), networkInfo.ldn.node_count);
- // Indicate a network error, as we do not actually emulate LDN
- rb.Push(static_cast<u32>(State::Error));
+ ctx.WriteBuffer<NetworkInfo>(networkInfo);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+}
- rb.Push(ResultSuccess);
+void IUserLocalCommunicationService::GetDisconnectReason(Kernel::HLERequestContext& ctx) {
+ const auto disconnect_reason = DisconnectReason::None;
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(disconnect_reason);
+}
+
+void IUserLocalCommunicationService::GetSecurityParameter(Kernel::HLERequestContext& ctx) {
+ SecurityParameter security_parameter;
+ NetworkInfo info;
+ const Result rc = ResultSuccess;
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
}
- void Initialize2(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_LDN, "called");
+ security_parameter.session_id = info.network_id.session_id;
+ std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
+ sizeof(SecurityParameter::data));
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
- is_initialized = true;
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<SecurityParameter>(security_parameter);
+}
+void IUserLocalCommunicationService::GetNetworkConfig(Kernel::HLERequestContext& ctx) {
+ NetworkConfig config;
+ NetworkInfo info;
+ const Result rc = ResultSuccess;
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(rc);
+ return;
}
-private:
- enum class State {
- None,
- Initialized,
- AccessPointOpened,
- AccessPointCreated,
- StationOpened,
- StationConnected,
- Error,
- };
-
- bool is_initialized{};
-};
+ config.intent_id = info.network_id.intent_id;
+ config.channel = info.common.channel;
+ config.node_count_max = info.ldn.node_count_max;
+ config.local_communication_version = info.ldn.nodes[0].local_communication_version;
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, "
+ "local_communication_version={}",
+ config.intent_id.local_communication_id, config.intent_id.scene_id, config.channel,
+ config.node_count_max, config.local_communication_version);
+
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<NetworkConfig>(config);
+}
+
+void IUserLocalCommunicationService::AttachStateChangeEvent(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(state_change_event->GetReadableEvent());
+}
+
+void IUserLocalCommunicationService::GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
+ const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
+ const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
+
+ if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size,
+ node_buffer_count);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo info;
+ std::vector<NodeLatestUpdate> latest_update{};
+ latest_update.resize(node_buffer_count);
+
+ const auto rc = ResultSuccess;
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
+ info.common.ssid.GetStringValue(), info.ldn.node_count);
+
+ ctx.WriteBuffer(info, 0);
+ ctx.WriteBuffer(latest_update, 1);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::Scan(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx);
+}
+
+void IUserLocalCommunicationService::ScanPrivate(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx, true);
+}
+
+void IUserLocalCommunicationService::ScanImpl(Kernel::HLERequestContext& ctx, bool is_private) {
+ IPC::RequestParser rp{ctx};
+ const auto channel{rp.PopEnum<WifiChannel>()};
+ const auto scan_filter{rp.PopRaw<ScanFilter>()};
+
+ const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
+
+ if (network_info_size == 0) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ u16 count = 0;
+ std::vector<NetworkInfo> networks_info{};
+ networks_info.resize(network_info_size);
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}",
+ channel, scan_filter.flag, scan_filter.network_type);
+
+ ctx.WriteBuffer(networks_info);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(count);
+}
+
+void IUserLocalCommunicationService::OpenAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::CloseAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::CreateNetwork(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ CreateNetworkImpl(ctx, false);
+}
+
+void IUserLocalCommunicationService::CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ CreateNetworkImpl(ctx, true);
+}
+
+void IUserLocalCommunicationService::CreateNetworkImpl(Kernel::HLERequestContext& ctx,
+ bool is_private) {
+ IPC::RequestParser rp{ctx};
+
+ const auto security_config{rp.PopRaw<SecurityConfig>()};
+ [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>()
+ : SecurityParameter{}};
+ const auto user_config{rp.PopRaw<UserConfig>()};
+ rp.Pop<u32>(); // Padding
+ const auto network_Config{rp.PopRaw<NetworkConfig>()};
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+void IUserLocalCommunicationService::DestroyNetwork(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::SetAdvertiseData(Kernel::HLERequestContext& ctx) {
+ std::vector<u8> read_buffer = ctx.ReadBuffer();
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::OpenStation(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::CloseStation(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::Connect(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::RequestParser rp{ctx};
+
+ [[maybe_unused]] const auto securityConfig{rp.PopRaw<SecurityConfig>()};
+ const auto user_config{rp.PopRaw<UserConfig>()};
+ const auto local_communication_version{rp.Pop<u32>()};
+ [[maybe_unused]] const auto option{rp.Pop<u32>()};
+
+ std::vector<u8> read_buffer = ctx.ReadBuffer();
+ NetworkInfo networkInfo{};
+
+ if (read_buffer.size() != sizeof(NetworkInfo)) {
+ LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ std::memcpy(&networkInfo, read_buffer.data(), read_buffer.size());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::Disconnect(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+void IUserLocalCommunicationService::Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ const auto rc = InitializeImpl(ctx);
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+}
+
+void IUserLocalCommunicationService::Finalize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ is_initialized = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUserLocalCommunicationService::Initialize2(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ const auto rc = InitializeImpl(ctx);
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+}
+
+Result IUserLocalCommunicationService::InitializeImpl(Kernel::HLERequestContext& ctx) {
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ if (!network_interface) {
+ return ResultAirplaneModeEnabled;
+ }
+
+ is_initialized = true;
+ // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented
+ return ResultAirplaneModeEnabled;
+}
class LDNS final : public ServiceFramework<LDNS> {
public:
@@ -273,7 +586,7 @@ public:
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(ResultDisabled);
}
};
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h
index a0031ac71..331455e3f 100644
--- a/src/core/hle/service/ldn/ldn.h
+++ b/src/core/hle/service/ldn/ldn.h
@@ -3,6 +3,14 @@
#pragma once
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/result.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/ldn/ldn_results.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/hle/service/sm/sm.h"
+
namespace Core {
class System;
}
@@ -16,4 +24,69 @@ namespace Service::LDN {
/// Registers all LDN services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+class IUserLocalCommunicationService final
+ : public ServiceFramework<IUserLocalCommunicationService> {
+public:
+ explicit IUserLocalCommunicationService(Core::System& system_);
+ ~IUserLocalCommunicationService() override;
+
+ void GetState(Kernel::HLERequestContext& ctx);
+
+ void GetNetworkInfo(Kernel::HLERequestContext& ctx);
+
+ void GetDisconnectReason(Kernel::HLERequestContext& ctx);
+
+ void GetSecurityParameter(Kernel::HLERequestContext& ctx);
+
+ void GetNetworkConfig(Kernel::HLERequestContext& ctx);
+
+ void AttachStateChangeEvent(Kernel::HLERequestContext& ctx);
+
+ void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx);
+
+ void Scan(Kernel::HLERequestContext& ctx);
+ void ScanPrivate(Kernel::HLERequestContext& ctx);
+ void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false);
+
+ void OpenAccessPoint(Kernel::HLERequestContext& ctx);
+
+ void CloseAccessPoint(Kernel::HLERequestContext& ctx);
+
+ void CreateNetwork(Kernel::HLERequestContext& ctx);
+ void CreateNetworkPrivate(Kernel::HLERequestContext& ctx);
+ void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private);
+
+ void DestroyNetwork(Kernel::HLERequestContext& ctx);
+
+ void SetAdvertiseData(Kernel::HLERequestContext& ctx);
+
+ void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx);
+
+ void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx);
+
+ void OpenStation(Kernel::HLERequestContext& ctx);
+
+ void CloseStation(Kernel::HLERequestContext& ctx);
+
+ void Disconnect(Kernel::HLERequestContext& ctx);
+
+ void Connect(Kernel::HLERequestContext& ctx);
+
+ void Initialize(Kernel::HLERequestContext& ctx);
+
+ void Finalize(Kernel::HLERequestContext& ctx);
+
+ void Initialize2(Kernel::HLERequestContext& ctx);
+ Result InitializeImpl(Kernel::HLERequestContext& ctx);
+
+private:
+ void OnEventFired();
+
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* state_change_event;
+ Network::RoomNetwork& room_network;
+
+ bool is_initialized{};
+};
+
} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h
new file mode 100644
index 000000000..8b6b436b7
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_results.h
@@ -0,0 +1,28 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::LDN {
+
+constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10};
+constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20};
+constexpr Result ResultDisabled{ErrorModule::LDN, 22};
+constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23};
+constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30};
+constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31};
+constexpr Result ResultBadState{ErrorModule::LDN, 32};
+constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33};
+constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50};
+constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65};
+constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66};
+constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67};
+constexpr Result ResultBadInput{ErrorModule::LDN, 96};
+constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97};
+constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113};
+constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
new file mode 100644
index 000000000..1132b2eb6
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -0,0 +1,298 @@
+// Copyright 2022 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <fmt/format.h>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "network/network.h"
+
+namespace Service::LDN {
+
+constexpr size_t SsidLengthMax = 32;
+constexpr size_t AdvertiseDataSizeMax = 384;
+constexpr size_t UserNameBytesMax = 32;
+constexpr int NodeCountMax = 8;
+constexpr int StationCountMax = NodeCountMax - 1;
+constexpr size_t PassphraseLengthMax = 64;
+
+enum class SecurityMode : u16 {
+ All,
+ Retail,
+ Debug,
+};
+
+enum class NodeStateChange : u8 {
+ None,
+ Connect,
+ Disconnect,
+ DisconnectAndConnect,
+};
+
+inline NodeStateChange operator|(NodeStateChange a, NodeStateChange b) {
+ return static_cast<NodeStateChange>(static_cast<u8>(a) | static_cast<u8>(b));
+}
+
+inline NodeStateChange operator|=(NodeStateChange& a, NodeStateChange b) {
+ return a = a | b;
+}
+
+enum class ScanFilterFlag : u32 {
+ None = 0,
+ LocalCommunicationId = 1 << 0,
+ SessionId = 1 << 1,
+ NetworkType = 1 << 2,
+ Ssid = 1 << 4,
+ SceneId = 1 << 5,
+ IntentId = LocalCommunicationId | SceneId,
+ NetworkId = IntentId | SessionId,
+};
+
+enum class NetworkType : u32 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class PackedNetworkType : u8 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class State : u32 {
+ None,
+ Initialized,
+ AccessPointOpened,
+ AccessPointCreated,
+ StationOpened,
+ StationConnected,
+ Error,
+};
+
+enum class DisconnectReason : s16 {
+ Unknown = -1,
+ None,
+ DisconnectedByUser,
+ DisconnectedBySystem,
+ DestroyedByUser,
+ DestroyedBySystem,
+ Rejected,
+ SignalLost,
+};
+
+enum class NetworkError {
+ Unknown = -1,
+ None = 0,
+ PortUnreachable,
+ TooManyPlayers,
+ VersionTooLow,
+ VersionTooHigh,
+ ConnectFailure,
+ ConnectNotFound,
+ ConnectTimeout,
+ ConnectRejected,
+ RejectFailed,
+};
+
+enum class AcceptPolicy : u8 {
+ AcceptAll,
+ RejectAll,
+ BlackList,
+ WhiteList,
+};
+
+enum class WifiChannel : s16 {
+ Default = 0,
+ wifi24_1 = 1,
+ wifi24_6 = 6,
+ wifi24_11 = 11,
+ wifi50_36 = 36,
+ wifi50_40 = 40,
+ wifi50_44 = 44,
+ wifi50_48 = 48,
+};
+
+enum class LinkLevel : s8 {
+ Bad,
+ Low,
+ Good,
+ Excelent,
+};
+
+struct NodeLatestUpdate {
+ NodeStateChange state_change;
+ INSERT_PADDING_BYTES(0x7); // Unknown
+};
+static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size");
+
+struct SessionId {
+ u64 high;
+ u64 low;
+
+public:
+ bool operator==(const SessionId& b) const {
+ return (low == b.low) && (high == b.high);
+ }
+};
+static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
+
+struct IntentId {
+ u64 local_communication_id;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 scene_id;
+ INSERT_PADDING_BYTES(0x4); // Reserved
+};
+static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
+
+struct NetworkId {
+ IntentId intent_id;
+ SessionId session_id;
+};
+static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
+
+struct Ssid {
+ u8 length;
+ std::array<char, SsidLengthMax + 1> raw;
+
+public:
+ std::string GetStringValue() const {
+ return std::string(raw.data(), length);
+ }
+};
+static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
+
+struct Ipv4Address {
+ union {
+ u32 raw{};
+ std::array<u8, 4> bytes;
+ };
+
+public:
+ std::string GetStringValue() const {
+ return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
+ }
+};
+static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
+
+struct MacAddress {
+ std::array<u8, 6> raw{};
+
+ friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
+};
+static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
+
+struct ScanFilter {
+ NetworkId network_id;
+ NetworkType network_type;
+ MacAddress mac_address;
+ Ssid ssid;
+ INSERT_PADDING_BYTES(0x10);
+ ScanFilterFlag flag;
+};
+static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size");
+
+struct CommonNetworkInfo {
+ MacAddress bssid;
+ Ssid ssid;
+ WifiChannel channel;
+ LinkLevel link_level;
+ PackedNetworkType network_type;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
+
+struct NodeInfo {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ s8 node_id;
+ u8 is_connected;
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ s16 local_communication_version;
+ INSERT_PADDING_BYTES(0x10); // Reserved
+};
+static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
+
+struct LdnNetworkInfo {
+ std::array<u8, 0x10> security_parameter;
+ SecurityMode security_mode;
+ AcceptPolicy station_accept_policy;
+ u8 has_action_frame;
+ INSERT_PADDING_BYTES(0x2); // Padding
+ u8 node_count_max;
+ u8 node_count;
+ std::array<NodeInfo, NodeCountMax> nodes;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 advertise_data_size;
+ std::array<u8, AdvertiseDataSizeMax> advertise_data;
+ INSERT_PADDING_BYTES(0x8C); // Reserved
+ u64 random_authentication_id;
+};
+static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
+
+struct NetworkInfo {
+ NetworkId network_id;
+ CommonNetworkInfo common;
+ LdnNetworkInfo ldn;
+};
+static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
+
+struct SecurityConfig {
+ SecurityMode security_mode;
+ u16 passphrase_size;
+ std::array<u8, PassphraseLengthMax> passphrase;
+};
+static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size");
+
+struct UserConfig {
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0xF); // Reserved
+};
+static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size");
+
+#pragma pack(push, 4)
+struct ConnectRequest {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ u32 local_communication_version;
+ u32 option_unknown;
+ NetworkInfo network_info;
+};
+static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size");
+#pragma pack(pop)
+
+struct SecurityParameter {
+ std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig
+ SessionId session_id;
+};
+static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size");
+
+struct NetworkConfig {
+ IntentId intent_id;
+ WifiChannel channel;
+ u8 node_count_max;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ u16 local_communication_version;
+ INSERT_PADDING_BYTES(0xA); // Reserved
+};
+static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size");
+
+struct AddressEntry {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+};
+static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size");
+
+struct AddressList {
+ std::array<AddressEntry, 0x8> addresses;
+};
+static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
+
+} // namespace Service::LDN