// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/service/psc/time/alarms.h" #include "core/hle/service/psc/time/manager.h" namespace Service::PSC::Time { Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type) : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} { m_event->Clear(); switch (type) { case WakeupAlarm: m_priority = 1; break; case BackgroundTaskAlarm: m_priority = 0; break; default: UNREACHABLE(); return; } } Alarm::~Alarm() { m_ctx.CloseEvent(m_event); } Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, PowerStateRequestManager& power_state_request_manager) : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock}, m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent( "Psc:Alarms:Event")} {} Alarms::~Alarms() { m_ctx.CloseEvent(m_event); } Result Alarms::Enable(Alarm& alarm, s64 time) { R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized); std::scoped_lock l{m_mutex}; R_UNLESS(alarm.IsLinked(), ResultAlarmNotRegistered); auto time_ns{time + m_steady_clock.GetRawTime()}; auto one_second_ns{ std::chrono::duration_cast(std::chrono::seconds(1)).count()}; time_ns = Common::AlignUp(time_ns, one_second_ns); alarm.SetAlertTime(time_ns); Insert(alarm); R_RETURN(UpdateClosestAndSignal()); } void Alarms::Disable(Alarm& alarm) { std::scoped_lock l{m_mutex}; if (!alarm.IsLinked()) { return; } Erase(alarm); UpdateClosestAndSignal(); } void Alarms::CheckAndSignal() { std::scoped_lock l{m_mutex}; if (m_alarms.empty()) { return; } bool alarm_signalled{false}; for (auto& alarm : m_alarms) { if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) { alarm.Signal(); alarm.Lock(); Erase(alarm); m_power_state_request_manager.UpdatePendingPowerStateRequestPriority( alarm.GetPriority()); alarm_signalled = true; } } if (!alarm_signalled) { return; } m_power_state_request_manager.SignalPowerStateRequestAvailability(); UpdateClosestAndSignal(); } bool Alarms::GetClosestAlarm(Alarm** out_alarm) { std::scoped_lock l{m_mutex}; auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); *out_alarm = alarm; return alarm != nullptr; } void Alarms::Insert(Alarm& alarm) { // Alarms are sorted by alert time, then priority auto it{m_alarms.begin()}; while (it != m_alarms.end()) { if (alarm.GetAlertTime() < it->GetAlertTime() || (alarm.GetAlertTime() == it->GetAlertTime() && alarm.GetPriority() < it->GetPriority())) { m_alarms.insert(it, alarm); return; } it++; } m_alarms.push_back(alarm); } void Alarms::Erase(Alarm& alarm) { m_alarms.erase(m_alarms.iterator_to(alarm)); } Result Alarms::UpdateClosestAndSignal() { m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); R_SUCCEED_IF(m_closest_alarm == nullptr); m_event->Signal(); R_SUCCEED(); } IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr manager) : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} { // clang-format off static const FunctionInfo functions[] = { {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"}, {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"}, }; // clang-format on RegisterHandlers(functions); } void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called."); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system, m_alarms, AlarmType::WakeupAlarm); } void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called."); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system, m_alarms, AlarmType::BackgroundTaskAlarm); } ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type) : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"}, m_alarms{alarms}, m_alarm{system, m_ctx, type} { // clang-format off static const FunctionInfo functions[] = { {0, &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"}, {1, &ISteadyClockAlarm::Enable, "Enable"}, {2, &ISteadyClockAlarm::Disable, "Disable"}, {3, &ISteadyClockAlarm::IsEnabled, "IsEnabled"}, {10, nullptr, "CreateWakeLock"}, {11, nullptr, "DestroyWakeLock"}, }; // clang-format on RegisterHandlers(functions); } void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called."); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); rb.PushCopyObjects(m_alarm.GetEventHandle()); } void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called."); IPC::RequestParser rp{ctx}; auto time{rp.Pop()}; auto res = m_alarms.Enable(m_alarm, time); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); } void ISteadyClockAlarm::Disable(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called."); m_alarms.Disable(m_alarm); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called."); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(m_alarm.IsLinked()); } } // namespace Service::PSC::Time