diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/core/core_timing_util.cpp | 84 | ||||
-rw-r--r-- | src/core/core_timing_util.h | 61 | ||||
-rw-r--r-- | src/core/frontend/applets/controller.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/am/am.cpp | 13 | ||||
-rw-r--r-- | src/core/hle/service/hid/controllers/npad.cpp | 63 | ||||
-rw-r--r-- | src/core/hle/service/hid/controllers/npad.h | 26 | ||||
-rw-r--r-- | src/core/hle/service/ldn/errors.h | 13 | ||||
-rw-r--r-- | src/core/hle/service/ldn/ldn.cpp | 36 |
9 files changed, 190 insertions, 110 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1662ec63d..c6bdf72ec 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -19,7 +19,6 @@ add_library(core STATIC core.h core_timing.cpp core_timing.h - core_timing_util.cpp core_timing_util.h cpu_manager.cpp cpu_manager.h @@ -266,6 +265,7 @@ add_library(core STATIC hle/service/am/applets/software_keyboard.h hle/service/am/applets/web_browser.cpp hle/service/am/applets/web_browser.h + hle/service/am/applets/web_types.h hle/service/am/idle.cpp hle/service/am/idle.h hle/service/am/omm.cpp @@ -400,6 +400,7 @@ add_library(core STATIC hle/service/hid/controllers/xpad.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h + hle/service/ldn/errors.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h hle/service/ldr/ldr.cpp diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp deleted file mode 100644 index 8ce8e602e..000000000 --- a/src/core/core_timing_util.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "core/core_timing_util.h" - -#include <cinttypes> -#include <limits> -#include "common/logging/log.h" -#include "common/uint128.h" -#include "core/hardware_properties.h" - -namespace Core::Timing { - -constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE; - -s64 msToCycles(std::chrono::milliseconds ms) { - if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) { - LOG_ERROR(Core_Timing, "Integer overflow, use max value"); - return std::numeric_limits<s64>::max(); - } - if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) { - LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return Hardware::BASE_CLOCK_RATE * (ms.count() / 1000); - } - return (Hardware::BASE_CLOCK_RATE * ms.count()) / 1000; -} - -s64 usToCycles(std::chrono::microseconds us) { - if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) { - LOG_ERROR(Core_Timing, "Integer overflow, use max value"); - return std::numeric_limits<s64>::max(); - } - if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) { - LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return Hardware::BASE_CLOCK_RATE * (us.count() / 1000000); - } - return (Hardware::BASE_CLOCK_RATE * us.count()) / 1000000; -} - -s64 nsToCycles(std::chrono::nanoseconds ns) { - const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE); - return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first; -} - -u64 msToClockCycles(std::chrono::milliseconds ns) { - const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); - return Common::Divide128On32(temp, 1000).first; -} - -u64 usToClockCycles(std::chrono::microseconds ns) { - const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); - return Common::Divide128On32(temp, 1000000).first; -} - -u64 nsToClockCycles(std::chrono::nanoseconds ns) { - const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); - return Common::Divide128On32(temp, 1000000000).first; -} - -u64 CpuCyclesToClockCycles(u64 ticks) { - const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ); - return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; -} - -std::chrono::milliseconds CyclesToMs(s64 cycles) { - const u128 temporal = Common::Multiply64Into128(cycles, 1000); - u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; - return std::chrono::milliseconds(ms); -} - -std::chrono::nanoseconds CyclesToNs(s64 cycles) { - const u128 temporal = Common::Multiply64Into128(cycles, 1000000000); - u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; - return std::chrono::nanoseconds(ns); -} - -std::chrono::microseconds CyclesToUs(s64 cycles) { - const u128 temporal = Common::Multiply64Into128(cycles, 1000000); - u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; - return std::chrono::microseconds(us); -} - -} // namespace Core::Timing diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index e4a046bf9..14c36a485 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -1,24 +1,59 @@ -// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project -// Licensed under GPLv2+ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include <chrono> + #include "common/common_types.h" +#include "core/hardware_properties.h" namespace Core::Timing { -s64 msToCycles(std::chrono::milliseconds ms); -s64 usToCycles(std::chrono::microseconds us); -s64 nsToCycles(std::chrono::nanoseconds ns); -u64 msToClockCycles(std::chrono::milliseconds ns); -u64 usToClockCycles(std::chrono::microseconds ns); -u64 nsToClockCycles(std::chrono::nanoseconds ns); -std::chrono::milliseconds CyclesToMs(s64 cycles); -std::chrono::nanoseconds CyclesToNs(s64 cycles); -std::chrono::microseconds CyclesToUs(s64 cycles); - -u64 CpuCyclesToClockCycles(u64 ticks); +namespace detail { +constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000; +constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000; +} // namespace detail + +[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) { + return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED; +} + +[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) { + return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000; +} + +[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) { + return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000; +} + +[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) { + return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED; +} + +[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) { + return us.count() * detail::CNTFREQ_ADJUSTED / 1000; +} + +[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) { + return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000; +} + +[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) { + return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED; +} + +[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) { + return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED); +} + +[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) { + return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED); +} + +[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) { + return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED); +} } // namespace Core::Timing diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index dff71d8d9..b0626a0f9 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h @@ -31,6 +31,7 @@ struct ControllerParameters { bool allow_dual_joycons{}; bool allow_left_joycon{}; bool allow_right_joycon{}; + bool allow_gamecube_controller{}; }; class ControllerApplet { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index bb77c2569..8e1fe9438 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1047,20 +1047,21 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop<u64>()}; const std::vector<u8> data{ctx.ReadBuffer()}; + const std::size_t size{std::min(data.size(), backing.GetSize() - offset)}; - LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); + LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); - if (data.size() > backing.GetSize() - offset) { + if (offset > backing.GetSize()) { LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", - backing.GetSize(), data.size(), offset); + backing.GetSize(), size, offset); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_SIZE_OUT_OF_BOUNDS); return; } - std::memcpy(backing.GetData().data() + offset, data.data(), data.size()); + std::memcpy(backing.GetData().data() + offset, data.data(), size); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -1070,11 +1071,11 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 offset{rp.Pop<u64>()}; - const std::size_t size{ctx.GetWriteBufferSize()}; + const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); - if (size > backing.GetSize() - offset) { + if (offset > backing.GetSize()) { LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", backing.GetSize(), size, offset); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index dbf198345..70b9f3824 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -21,6 +21,7 @@ namespace Service::HID { constexpr s32 HID_JOYSTICK_MAX = 0x7fff; +constexpr s32 HID_TRIGGER_MAX = 0x7fff; [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; constexpr std::size_t NPAD_OFFSET = 0x9A00; constexpr u32 BATTERY_FULL = 2; @@ -48,6 +49,8 @@ Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad( return NPadControllerType::JoyRight; case Settings::ControllerType::Handheld: return NPadControllerType::Handheld; + case Settings::ControllerType::GameCube: + return NPadControllerType::GameCube; default: UNREACHABLE(); return NPadControllerType::ProController; @@ -67,6 +70,8 @@ Settings::ControllerType Controller_NPad::MapNPadToSettingsType( return Settings::ControllerType::RightJoycon; case NPadControllerType::Handheld: return Settings::ControllerType::Handheld; + case NPadControllerType::GameCube: + return Settings::ControllerType::GameCube; default: UNREACHABLE(); return Settings::ControllerType::ProController; @@ -209,6 +214,13 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.assignment_mode = NpadAssignments::Single; controller.footer_type = AppletFooterUiType::JoyRightHorizontal; break; + case NPadControllerType::GameCube: + controller.style_set.gamecube.Assign(1); + // The GC Controller behaves like a wired Pro Controller + controller.device_type.fullkey.Assign(1); + controller.system_properties.is_vertical.Assign(1); + controller.system_properties.use_plus.Assign(1); + break; case NPadControllerType::Pokeball: controller.style_set.palma.Assign(1); controller.device_type.palma.Assign(1); @@ -259,6 +271,7 @@ void Controller_NPad::OnInit() { style.joycon_right.Assign(1); style.joycon_dual.Assign(1); style.fullkey.Assign(1); + style.gamecube.Assign(1); style.palma.Assign(1); } @@ -339,6 +352,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { auto& pad_state = npad_pad_states[controller_idx].pad_states; auto& lstick_entry = npad_pad_states[controller_idx].l_stick; auto& rstick_entry = npad_pad_states[controller_idx].r_stick; + auto& trigger_entry = npad_trigger_states[controller_idx]; const auto& button_state = buttons[controller_idx]; const auto& analog_state = sticks[controller_idx]; const auto [stick_l_x_f, stick_l_y_f] = @@ -404,6 +418,17 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); } + + if (controller_type == NPadControllerType::GameCube) { + trigger_entry.l_analog = static_cast<s32>( + button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0); + trigger_entry.r_analog = static_cast<s32>( + button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0); + pad_state.zl.Assign(false); + pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); + } } void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, @@ -418,6 +443,11 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states, &npad.system_ext_states}; + // There is the posibility to have more controllers with analog triggers + const std::array<TriggerGeneric*, 1> controller_triggers{ + &npad.gc_trigger_states, + }; + for (auto* main_controller : controller_npads) { main_controller->common.entry_count = 16; main_controller->common.total_entry_count = 17; @@ -435,6 +465,21 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* cur_entry.timestamp2 = cur_entry.timestamp; } + for (auto* analog_trigger : controller_triggers) { + analog_trigger->entry_count = 16; + analog_trigger->total_entry_count = 17; + + const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; + + analog_trigger->timestamp = core_timing.GetCPUTicks(); + analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17; + + auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; + + cur_entry.timestamp = last_entry.timestamp + 1; + cur_entry.timestamp2 = cur_entry.timestamp; + } + const auto& controller_type = connected_controllers[i].type; if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { @@ -444,6 +489,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* RequestPadStateUpdate(npad_index); auto& pad_state = npad_pad_states[npad_index]; + auto& trigger_state = npad_trigger_states[npad_index]; auto& main_controller = npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index]; @@ -456,6 +502,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index]; auto& libnx_entry = npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index]; + auto& trigger_entry = + npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index]; libnx_entry.connection_status.raw = 0; libnx_entry.connection_status.is_connected.Assign(1); @@ -524,6 +572,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_entry.connection_status.is_right_connected.Assign(1); break; + case NPadControllerType::GameCube: + main_controller.connection_status.raw = 0; + main_controller.connection_status.is_connected.Assign(1); + main_controller.connection_status.is_wired.Assign(1); + main_controller.pad.pad_states.raw = pad_state.pad_states.raw; + main_controller.pad.l_stick = pad_state.l_stick; + main_controller.pad.r_stick = pad_state.r_stick; + trigger_entry.l_analog = trigger_state.l_analog; + trigger_entry.r_analog = trigger_state.r_analog; + + libnx_entry.connection_status.is_wired.Assign(1); + break; case NPadControllerType::Pokeball: pokeball_entry.connection_status.raw = 0; pokeball_entry.connection_status.is_connected.Assign(1); @@ -674,6 +734,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing right_sixaxis_entry.orientation = motion_devices[1].orientation; } break; + case NPadControllerType::GameCube: case NPadControllerType::Pokeball: break; } @@ -1135,6 +1196,8 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const return style.joycon_left; case NPadControllerType::JoyRight: return style.joycon_right; + case NPadControllerType::GameCube: + return style.gamecube; case NPadControllerType::Pokeball: return style.palma; default: diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 48bab988c..bc2e6779d 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -51,6 +51,7 @@ public: JoyDual, JoyLeft, JoyRight, + GameCube, Pokeball, }; @@ -60,6 +61,7 @@ public: JoyconDual = 5, JoyconLeft = 6, JoyconRight = 7, + GameCube = 8, Pokeball = 9, MaxNpadType = 10, }; @@ -389,6 +391,25 @@ private: }; static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size"); + struct TriggerState { + s64_le timestamp{}; + s64_le timestamp2{}; + s32_le l_analog{}; + s32_le r_analog{}; + }; + static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size"); + + struct TriggerGeneric { + INSERT_PADDING_BYTES(0x4); + s64_le timestamp; + INSERT_PADDING_BYTES(0x4); + s64_le total_entry_count; + s64_le last_entry_index; + s64_le entry_count; + std::array<TriggerState, 17> trigger{}; + }; + static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size"); + struct NPadSystemProperties { union { s64_le raw{}; @@ -509,7 +530,9 @@ private: AppletFooterUiType footer_type; // nfc_states needs to be checked switchbrew does not match with HW NfcXcdHandle nfc_states; - INSERT_PADDING_BYTES(0xdef); + INSERT_PADDING_BYTES(0x8); // Mutex + TriggerGeneric gc_trigger_states; + INSERT_PADDING_BYTES(0xc1f); }; static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); @@ -560,6 +583,7 @@ private: f32 sixaxis_fusion_parameter2{}; bool sixaxis_at_rest{true}; std::array<ControllerPad, 10> npad_pad_states{}; + std::array<TriggerState, 10> npad_trigger_states{}; bool is_in_lr_assignment_mode{false}; Core::System& system; }; diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h new file mode 100644 index 000000000..a718c5c66 --- /dev/null +++ b/src/core/hle/service/ldn/errors.h @@ -0,0 +1,13 @@ +// Copyright 2021 yuzu emulator team +// 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 ResultCode 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 ee908f399..c630d93cd 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -6,6 +6,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/result.h" +#include "core/hle/service/ldn/errors.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/sm/sm.h" @@ -103,7 +104,7 @@ public: : ServiceFramework{system_, "IUserLocalCommunicationService"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "GetState"}, + {0, &IUserLocalCommunicationService::GetState, "GetState"}, {1, nullptr, "GetNetworkInfo"}, {2, nullptr, "GetIpv4Address"}, {3, nullptr, "GetDisconnectReason"}, @@ -138,13 +139,38 @@ public: RegisterHandlers(functions); } - void Initialize2(Kernel::HLERequestContext& ctx) { + void GetState(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDN, "(STUBBED) called"); - // Result success seem make this services start network and continue. - // If we just pass result error then it will stop and maybe try again and again. + + IPC::ResponseBuilder rb{ctx, 3}; + + // Indicate a network error, as we do not actually emulate LDN + rb.Push(static_cast<u32>(State::Error)); + + rb.Push(RESULT_SUCCESS); + } + + void Initialize2(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LDN, "called"); + + is_initialized = true; + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_UNKNOWN); + rb.Push(RESULT_SUCCESS); } + +private: + enum class State { + None, + Initialized, + AccessPointOpened, + AccessPointCreated, + StationOpened, + StationConnected, + Error, + }; + + bool is_initialized{}; }; class LDNS final : public ServiceFramework<LDNS> { |