// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include "core/core.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldn/ldn_results.h" #include "core/hle/service/ldn/ldn_types.h" #include "core/internal_network/network.h" #include "core/internal_network/network_interface.h" // This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent #undef CreateEvent namespace Service::LDN { class IMonitorService final : public ServiceFramework { public: explicit IMonitorService(Core::System& system_) : ServiceFramework{system_, "IMonitorService"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetStateForMonitor"}, {1, nullptr, "GetNetworkInfoForMonitor"}, {2, nullptr, "GetIpv4AddressForMonitor"}, {3, nullptr, "GetDisconnectReasonForMonitor"}, {4, nullptr, "GetSecurityParameterForMonitor"}, {5, nullptr, "GetNetworkConfigForMonitor"}, {100, nullptr, "InitializeMonitor"}, {101, nullptr, "FinalizeMonitor"}, }; // clang-format on RegisterHandlers(functions); } }; class LDNM final : public ServiceFramework { public: explicit LDNM(Core::System& system_) : ServiceFramework{system_, "ldn:m"} { // clang-format off static const FunctionInfo functions[] = { {0, &LDNM::CreateMonitorService, "CreateMonitorService"} }; // clang-format on RegisterHandlers(functions); } void CreateMonitorService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system); } }; class ISystemLocalCommunicationService final : public ServiceFramework { public: explicit ISystemLocalCommunicationService(Core::System& system_) : ServiceFramework{system_, "ISystemLocalCommunicationService"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetState"}, {1, nullptr, "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"}, {104, nullptr, "SetWirelessControllerRestriction"}, {200, nullptr, "OpenAccessPoint"}, {201, nullptr, "CloseAccessPoint"}, {202, nullptr, "CreateNetwork"}, {203, nullptr, "CreateNetworkPrivate"}, {204, nullptr, "DestroyNetwork"}, {205, nullptr, "Reject"}, {206, nullptr, "SetAdvertiseData"}, {207, nullptr, "SetStationAcceptPolicy"}, {208, nullptr, "AddAcceptFilterEntry"}, {209, nullptr, "ClearAcceptFilter"}, {300, nullptr, "OpenStation"}, {301, nullptr, "CloseStation"}, {302, nullptr, "Connect"}, {303, nullptr, "ConnectPrivate"}, {304, nullptr, "Disconnect"}, {400, nullptr, "InitializeSystem"}, {401, nullptr, "FinalizeSystem"}, {402, nullptr, "SetOperationMode"}, {403, nullptr, "InitializeSystem2"}, }; // clang-format on RegisterHandlers(functions); } }; class IUserLocalCommunicationService final : public ServiceFramework { public: explicit 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, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, {2, nullptr, "GetIpv4Address"}, {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, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"}, {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"}, {205, nullptr, "Reject"}, {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"}, {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"}, {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"}, {209, nullptr, "ClearAcceptFilter"}, {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"}, {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"}, {302, &IUserLocalCommunicationService::Connect, "Connect"}, {303, nullptr, "ConnectPrivate"}, {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"}, {400, &IUserLocalCommunicationService::Initialize, "Initialize"}, {401, &IUserLocalCommunicationService::Finalize, "Finalize"}, {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, }; // clang-format on RegisterHandlers(functions); state_change_event = service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent"); } ~IUserLocalCommunicationService() { service_context.CloseEvent(state_change_event); } void OnEventFired() { state_change_event->GetWritableEvent().Signal(); } void 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 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; } NetworkInfo network_info{}; 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={}", network_info.common.ssid.GetStringValue(), network_info.ldn.node_count); ctx.WriteBuffer(network_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); } void 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 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; } 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"); IPC::ResponseBuilder rb{ctx, 10}; rb.Push(rc); rb.PushRaw(security_parameter); } void 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(rc); return; } 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(config); } void 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 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 latest_update(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 Scan(Kernel::HLERequestContext& ctx) { ScanImpl(ctx); } void ScanPrivate(Kernel::HLERequestContext& ctx) { ScanImpl(ctx, true); } void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { IPC::RequestParser rp{ctx}; const auto channel{rp.PopEnum()}; const auto scan_filter{rp.PopRaw()}; 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 network_infos(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(network_infos); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(count); } void OpenAccessPoint(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void CloseAccessPoint(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void CreateNetwork(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { SecurityConfig security_config; UserConfig user_config; INSERT_PADDING_WORDS_NOINIT(1); NetworkConfig network_config; }; static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size."); const auto parameters{rp.PopRaw()}; LOG_WARNING(Service_LDN, "(STUBBED) called, passphrase_size={}, security_mode={}, " "local_communication_version={}", parameters.security_config.passphrase_size, parameters.security_config.security_mode, parameters.network_config.local_communication_version); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { SecurityConfig security_config; SecurityParameter security_parameter; UserConfig user_config; NetworkConfig network_config; }; static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size."); const auto parameters{rp.PopRaw()}; LOG_WARNING(Service_LDN, "(STUBBED) called, passphrase_size={}, security_mode={}, " "local_communication_version={}", parameters.security_config.passphrase_size, parameters.security_config.security_mode, parameters.network_config.local_communication_version); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void DestroyNetwork(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void SetAdvertiseData(Kernel::HLERequestContext& ctx) { std::vector read_buffer = ctx.ReadBuffer(); LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void OpenStation(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void CloseStation(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Connect(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { SecurityConfig security_config; UserConfig user_config; u32 local_communication_version; u32 option; }; static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size."); const auto parameters{rp.PopRaw()}; LOG_WARNING(Service_LDN, "(STUBBED) called, passphrase_size={}, security_mode={}, " "local_communication_version={}", parameters.security_config.passphrase_size, parameters.security_config.security_mode, parameters.local_communication_version); const std::vector read_buffer = ctx.ReadBuffer(); NetworkInfo network_info{}; 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(&network_info, read_buffer.data(), read_buffer.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Disconnect(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); const auto rc = InitializeImpl(ctx); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); } void Finalize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); is_initialized = false; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Initialize2(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); const auto rc = InitializeImpl(ctx); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); } Result InitializeImpl(Kernel::HLERequestContext& ctx) { const auto network_interface = Network::GetSelectedNetworkInterface(); if (!network_interface) { LOG_ERROR(Service_LDN, "No network interface is set"); return ResultAirplaneModeEnabled; } is_initialized = true; // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented return ResultAirplaneModeEnabled; } KernelHelpers::ServiceContext service_context; Kernel::KEvent* state_change_event; Network::RoomNetwork& room_network; bool is_initialized{}; }; class LDNS final : public ServiceFramework { public: explicit LDNS(Core::System& system_) : ServiceFramework{system_, "ldn:s"} { // clang-format off static const FunctionInfo functions[] = { {0, &LDNS::CreateSystemLocalCommunicationService, "CreateSystemLocalCommunicationService"}, }; // clang-format on RegisterHandlers(functions); } void CreateSystemLocalCommunicationService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system); } }; class LDNU final : public ServiceFramework { public: explicit LDNU(Core::System& system_) : ServiceFramework{system_, "ldn:u"} { // clang-format off static const FunctionInfo functions[] = { {0, &LDNU::CreateUserLocalCommunicationService, "CreateUserLocalCommunicationService"}, }; // clang-format on RegisterHandlers(functions); } void CreateUserLocalCommunicationService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_LDN, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system); } }; class INetworkService final : public ServiceFramework { public: explicit INetworkService(Core::System& system_) : ServiceFramework{system_, "INetworkService"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Initialize"}, {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, {264, nullptr, "GetNetworkInterfaceLastError"}, {272, nullptr, "GetRole"}, {280, nullptr, "GetAdvertiseData"}, {288, nullptr, "GetGroupInfo"}, {296, nullptr, "GetGroupInfo2"}, {304, nullptr, "GetGroupOwner"}, {312, nullptr, "GetIpConfig"}, {320, nullptr, "GetLinkLevel"}, {512, nullptr, "Scan"}, {768, nullptr, "CreateGroup"}, {776, nullptr, "DestroyGroup"}, {784, nullptr, "SetAdvertiseData"}, {1536, nullptr, "SendToOtherGroup"}, {1544, nullptr, "RecvFromOtherGroup"}, {1552, nullptr, "AddAcceptableGroupId"}, {1560, nullptr, "ClearAcceptableGroupId"}, }; // clang-format on RegisterHandlers(functions); } }; class INetworkServiceMonitor final : public ServiceFramework { public: explicit INetworkServiceMonitor(Core::System& system_) : ServiceFramework{system_, "INetworkServiceMonitor"} { // clang-format off static const FunctionInfo functions[] = { {0, &INetworkServiceMonitor::Initialize, "Initialize"}, {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, {264, nullptr, "GetNetworkInterfaceLastError"}, {272, nullptr, "GetRole"}, {280, nullptr, "GetAdvertiseData"}, {281, nullptr, "GetAdvertiseData2"}, {288, nullptr, "GetGroupInfo"}, {296, nullptr, "GetGroupInfo2"}, {304, nullptr, "GetGroupOwner"}, {312, nullptr, "GetIpConfig"}, {320, nullptr, "GetLinkLevel"}, {328, nullptr, "AttachJoinEvent"}, {336, nullptr, "GetMembers"}, }; // clang-format on RegisterHandlers(functions); } void Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultDisabled); } }; class LP2PAPP final : public ServiceFramework { public: explicit LP2PAPP(Core::System& system_) : ServiceFramework{system_, "lp2p:app"} { // clang-format off static const FunctionInfo functions[] = { {0, &LP2PAPP::CreateMonitorService, "CreateNetworkService"}, {8, &LP2PAPP::CreateMonitorService, "CreateNetworkServiceMonitor"}, }; // clang-format on RegisterHandlers(functions); } void CreateNetworkervice(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 reserved_input = rp.Pop(); const u32 input = rp.Pop(); LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input, input); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system); } void CreateMonitorService(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 reserved_input = rp.Pop(); LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system); } }; class LP2PSYS final : public ServiceFramework { public: explicit LP2PSYS(Core::System& system_) : ServiceFramework{system_, "lp2p:sys"} { // clang-format off static const FunctionInfo functions[] = { {0, &LP2PSYS::CreateMonitorService, "CreateNetworkService"}, {8, &LP2PSYS::CreateMonitorService, "CreateNetworkServiceMonitor"}, }; // clang-format on RegisterHandlers(functions); } void CreateNetworkervice(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 reserved_input = rp.Pop(); const u32 input = rp.Pop(); LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input, input); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system); } void CreateMonitorService(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 reserved_input = rp.Pop(); LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system); } }; void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared(system)->InstallAsService(sm); std::make_shared(system)->InstallAsService(sm); std::make_shared(system)->InstallAsService(sm); std::make_shared(system)->InstallAsService(sm); std::make_shared(system)->InstallAsService(sm); } } // namespace Service::LDN