339 lines
13 KiB
C++
Executable File
339 lines
13 KiB
C++
Executable File
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "common/scope_exit.h"
|
|
#include "core/core.h"
|
|
#include "core/core_timing.h"
|
|
#include "core/hle/service/glue/time/file_timestamp_worker.h"
|
|
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
|
|
#include "core/hle/service/glue/time/worker.h"
|
|
#include "core/hle/service/psc/time/common.h"
|
|
#include "core/hle/service/psc/time/service_manager.h"
|
|
#include "core/hle/service/psc/time/static.h"
|
|
#include "core/hle/service/psc/time/system_clock.h"
|
|
#include "core/hle/service/set/system_settings_server.h"
|
|
#include "core/hle/service/sm/sm.h"
|
|
|
|
namespace Service::Glue::Time {
|
|
namespace {
|
|
|
|
bool g_ig_report_network_clock_context_set{};
|
|
Service::PSC::Time::SystemClockContext g_report_network_clock_context{};
|
|
bool g_ig_report_ephemeral_clock_context_set{};
|
|
Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{};
|
|
|
|
template <typename T>
|
|
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
|
|
const char* category, const char* name) {
|
|
std::vector<u8> interval_buf;
|
|
auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
|
|
ASSERT(res == ResultSuccess);
|
|
|
|
T v{};
|
|
std::memcpy(&v, interval_buf.data(), sizeof(T));
|
|
return v;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
|
|
FileTimestampWorker& file_timestamp_worker)
|
|
: m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")},
|
|
m_steady_clock_resource{steady_clock_resource},
|
|
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
|
|
"Glue:58:SteadyClockTimerEvent")},
|
|
m_timer_file_system{m_ctx.CreateEvent("Glue:58:FileTimeTimerEvent")},
|
|
m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
|
|
g_ig_report_network_clock_context_set = false;
|
|
g_report_network_clock_context = {};
|
|
g_ig_report_ephemeral_clock_context_set = false;
|
|
g_report_ephemeral_clock_context = {};
|
|
|
|
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
|
|
"Time::SteadyClockEvent",
|
|
[this](s64 time,
|
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
|
m_timer_steady_clock->Signal();
|
|
return std::nullopt;
|
|
});
|
|
|
|
m_timer_file_system_timing_event = Core::Timing::CreateEvent(
|
|
"Time::SteadyClockEvent",
|
|
[this](s64 time,
|
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
|
m_timer_file_system->Signal();
|
|
return std::nullopt;
|
|
});
|
|
}
|
|
|
|
TimeWorker::~TimeWorker() {
|
|
m_local_clock_event->Signal();
|
|
m_network_clock_event->Signal();
|
|
m_ephemeral_clock_event->Signal();
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
|
|
|
m_thread.request_stop();
|
|
m_event->Signal();
|
|
m_thread.join();
|
|
|
|
m_ctx.CloseEvent(m_event);
|
|
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
|
|
m_ctx.CloseEvent(m_timer_steady_clock);
|
|
m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event);
|
|
m_ctx.CloseEvent(m_timer_file_system);
|
|
}
|
|
|
|
void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
|
|
std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) {
|
|
m_set_sys = std::move(set_sys);
|
|
m_time_m =
|
|
m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
|
|
m_time_sm = std::move(time_sm);
|
|
|
|
m_alarm_worker.Initialize(m_time_m);
|
|
|
|
auto steady_clock_interval_m = GetSettingsItemValue<s32>(
|
|
m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes");
|
|
|
|
auto one_minute_ns{
|
|
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
|
|
s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns};
|
|
|
|
m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
|
|
std::chrono::nanoseconds(steady_clock_interval_ns),
|
|
m_timer_steady_clock_timing_event);
|
|
|
|
auto fs_notify_time_s =
|
|
GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds");
|
|
auto one_second_ns{
|
|
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
|
|
s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns};
|
|
|
|
m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
|
|
std::chrono::nanoseconds(fs_notify_time_ns),
|
|
m_timer_file_system_timing_event);
|
|
|
|
auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock);
|
|
ASSERT(res == ResultSuccess);
|
|
res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event);
|
|
ASSERT(res == ResultSuccess);
|
|
|
|
res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock);
|
|
ASSERT(res == ResultSuccess);
|
|
res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event);
|
|
ASSERT(res == ResultSuccess);
|
|
|
|
res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock);
|
|
ASSERT(res == ResultSuccess);
|
|
res =
|
|
m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event);
|
|
ASSERT(res == ResultSuccess);
|
|
|
|
res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
|
|
&m_standard_user_auto_correct_clock_event);
|
|
ASSERT(res == ResultSuccess);
|
|
}
|
|
|
|
void TimeWorker::StartThread() {
|
|
m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
|
|
}
|
|
|
|
void TimeWorker::ThreadFunc(std::stop_token stop_token) {
|
|
Common::SetCurrentThreadName("TimeWorker");
|
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
|
|
|
|
enum class EventType {
|
|
Exit = 0,
|
|
IpmModuleService_GetEvent = 1,
|
|
PowerStateChange = 2,
|
|
SignalAlarms = 3,
|
|
UpdateLocalSystemClock = 4,
|
|
UpdateNetworkSystemClock = 5,
|
|
UpdateEphemeralSystemClock = 6,
|
|
UpdateSteadyClock = 7,
|
|
UpdateFileTimestamp = 8,
|
|
AutoCorrect = 9,
|
|
Max = 10,
|
|
};
|
|
|
|
s32 num_objs{};
|
|
std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
|
|
std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
|
|
|
|
const auto AddWaiter{
|
|
[&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
|
|
// Open a new reference to the object.
|
|
synchronization_object->Open();
|
|
|
|
// Insert into the list.
|
|
wait_indices[num_objs] = type;
|
|
wait_objs[num_objs++] = synchronization_object;
|
|
}};
|
|
|
|
while (!stop_token.stop_requested()) {
|
|
SCOPE_EXIT({
|
|
for (s32 i = 0; i < num_objs; i++) {
|
|
wait_objs[i]->Close();
|
|
}
|
|
});
|
|
|
|
num_objs = {};
|
|
wait_objs = {};
|
|
if (m_pm_state_change_handler.m_priority != 0) {
|
|
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
|
|
// TODO
|
|
// AddWaiter(gIPmModuleService::GetEvent(), 1);
|
|
AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
|
|
} else {
|
|
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
|
|
// TODO
|
|
// AddWaiter(gIPmModuleService::GetEvent(), 1);
|
|
AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
|
|
AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
|
|
AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock);
|
|
AddWaiter(&m_network_clock_event->GetReadableEvent(),
|
|
EventType::UpdateNetworkSystemClock);
|
|
AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(),
|
|
EventType::UpdateEphemeralSystemClock);
|
|
AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
|
|
AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
|
|
AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(),
|
|
EventType::AutoCorrect);
|
|
}
|
|
|
|
s32 out_index{-1};
|
|
Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
|
|
num_objs, -1);
|
|
ASSERT(out_index >= 0 && out_index < num_objs);
|
|
|
|
if (stop_token.stop_requested()) {
|
|
return;
|
|
}
|
|
|
|
switch (wait_indices[out_index]) {
|
|
case EventType::Exit:
|
|
return;
|
|
|
|
case EventType::IpmModuleService_GetEvent:
|
|
// TODO
|
|
// IPmModuleService::GetEvent()
|
|
// clear the event
|
|
// Handle power state change event
|
|
break;
|
|
|
|
case EventType::PowerStateChange:
|
|
m_alarm_worker.GetEvent().Clear();
|
|
if (m_pm_state_change_handler.m_priority <= 1) {
|
|
m_alarm_worker.OnPowerStateChanged();
|
|
}
|
|
break;
|
|
|
|
case EventType::SignalAlarms:
|
|
m_alarm_worker.GetTimerEvent().Clear();
|
|
m_time_m->CheckAndSignalAlarms();
|
|
break;
|
|
|
|
case EventType::UpdateLocalSystemClock: {
|
|
m_local_clock_event->Clear();
|
|
|
|
Service::PSC::Time::SystemClockContext context{};
|
|
auto res = m_local_clock->GetSystemClockContext(context);
|
|
ASSERT(res == ResultSuccess);
|
|
|
|
m_set_sys->SetUserSystemClockContext(context);
|
|
|
|
m_file_timestamp_worker.SetFilesystemPosixTime();
|
|
} break;
|
|
|
|
case EventType::UpdateNetworkSystemClock: {
|
|
m_network_clock_event->Clear();
|
|
Service::PSC::Time::SystemClockContext context{};
|
|
auto res = m_network_clock->GetSystemClockContext(context);
|
|
ASSERT(res == ResultSuccess);
|
|
m_set_sys->SetNetworkSystemClockContext(context);
|
|
|
|
s64 time{};
|
|
if (m_network_clock->GetCurrentTime(time) != ResultSuccess) {
|
|
break;
|
|
}
|
|
|
|
[[maybe_unused]] auto offset_before{
|
|
g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0};
|
|
// TODO system report "standard_netclock_operation"
|
|
// "clock_time" = time
|
|
// "context_offset_before" = offset_before
|
|
// "context_offset_after" = context.offset
|
|
g_report_network_clock_context = context;
|
|
if (!g_ig_report_network_clock_context_set) {
|
|
g_ig_report_network_clock_context_set = true;
|
|
}
|
|
|
|
m_file_timestamp_worker.SetFilesystemPosixTime();
|
|
} break;
|
|
|
|
case EventType::UpdateEphemeralSystemClock: {
|
|
m_ephemeral_clock_event->Clear();
|
|
|
|
Service::PSC::Time::SystemClockContext context{};
|
|
auto res = m_ephemeral_clock->GetSystemClockContext(context);
|
|
if (res != ResultSuccess) {
|
|
break;
|
|
}
|
|
|
|
s64 time{};
|
|
res = m_ephemeral_clock->GetCurrentTime(time);
|
|
if (res != ResultSuccess) {
|
|
break;
|
|
}
|
|
|
|
[[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set
|
|
? g_report_ephemeral_clock_context.offset
|
|
: 0};
|
|
// TODO system report "ephemeral_netclock_operation"
|
|
// "clock_time" = time
|
|
// "context_offset_before" = offset_before
|
|
// "context_offset_after" = context.offset
|
|
g_report_ephemeral_clock_context = context;
|
|
if (!g_ig_report_ephemeral_clock_context_set) {
|
|
g_ig_report_ephemeral_clock_context_set = true;
|
|
}
|
|
} break;
|
|
|
|
case EventType::UpdateSteadyClock:
|
|
m_timer_steady_clock->Clear();
|
|
|
|
m_steady_clock_resource.UpdateTime();
|
|
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
|
|
break;
|
|
|
|
case EventType::UpdateFileTimestamp:
|
|
m_timer_file_system->Clear();
|
|
|
|
m_file_timestamp_worker.SetFilesystemPosixTime();
|
|
break;
|
|
|
|
case EventType::AutoCorrect: {
|
|
m_standard_user_auto_correct_clock_event->Clear();
|
|
|
|
bool automatic_correction{};
|
|
auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
|
|
automatic_correction);
|
|
ASSERT(res == ResultSuccess);
|
|
|
|
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
|
res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
|
|
ASSERT(res == ResultSuccess);
|
|
|
|
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
|
|
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
|
|
} break;
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Service::Glue::Time
|