summaryrefslogtreecommitdiff
path: root/src/core/hle/service/glue
diff options
context:
space:
mode:
authorGravatar Kelebek12023-10-29 13:50:55 +0000
committerGravatar Kelebek12024-01-24 04:26:55 +0000
commite4915fb7d2077584a11a15141bc81d28ed2b0125 (patch)
tree1783055dc2e98eaf9099e8e7b194b55f8f607747 /src/core/hle/service/glue
parentMerge pull request #12678 from german77/settings_impl (diff)
downloadyuzu-e4915fb7d2077584a11a15141bc81d28ed2b0125.tar.gz
yuzu-e4915fb7d2077584a11a15141bc81d28ed2b0125.tar.xz
yuzu-e4915fb7d2077584a11a15141bc81d28ed2b0125.zip
Rework time service to fix time passing offline.
Diffstat (limited to 'src/core/hle/service/glue')
-rw-r--r--src/core/hle/service/glue/glue.cpp19
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.cpp82
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.h53
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.cpp23
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.h28
-rw-r--r--src/core/hle/service/glue/time/manager.cpp277
-rw-r--r--src/core/hle/service/glue/time/manager.h42
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.cpp13
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.h18
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.cpp123
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.h41
-rw-r--r--src/core/hle/service/glue/time/static.cpp448
-rw-r--r--src/core/hle/service/glue/time/static.h110
-rw-r--r--src/core/hle/service/glue/time/time_zone.cpp377
-rw-r--r--src/core/hle/service/glue/time/time_zone.h95
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.cpp221
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.h32
-rw-r--r--src/core/hle/service/glue/time/worker.cpp338
-rw-r--r--src/core/hle/service/glue/time/worker.h64
19 files changed, 2404 insertions, 0 deletions
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index 993c3d21d..10376bfac 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,9 @@
8#include "core/hle/service/glue/ectx.h" 8#include "core/hle/service/glue/ectx.h"
9#include "core/hle/service/glue/glue.h" 9#include "core/hle/service/glue/glue.h"
10#include "core/hle/service/glue/notif.h" 10#include "core/hle/service/glue/notif.h"
11#include "core/hle/service/glue/time/manager.h"
12#include "core/hle/service/glue/time/static.h"
13#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/server_manager.h" 14#include "core/hle/service/server_manager.h"
12 15
13namespace Service::Glue { 16namespace Service::Glue {
@@ -31,6 +34,22 @@ void LoopProcess(Core::System& system) {
31 // Notification Services for application 34 // Notification Services for application
32 server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system)); 35 server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system));
33 36
37 // Time
38 auto time = std::make_shared<Time::TimeManager>(system);
39
40 server_manager->RegisterNamedService(
41 "time:u",
42 std::make_shared<Time::StaticService>(
43 system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u"));
44 server_manager->RegisterNamedService(
45 "time:a",
46 std::make_shared<Time::StaticService>(
47 system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a"));
48 server_manager->RegisterNamedService(
49 "time:r",
50 std::make_shared<Time::StaticService>(
51 system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r"));
52
34 ServerManager::RunServer(std::move(server_manager)); 53 ServerManager::RunServer(std::move(server_manager));
35} 54}
36 55
diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp
new file mode 100644
index 000000000..f549ed00a
--- /dev/null
+++ b/src/core/hle/service/glue/time/alarm_worker.cpp
@@ -0,0 +1,82 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/svc.h"
7#include "core/hle/service/glue/time/alarm_worker.h"
8#include "core/hle/service/psc/time/service_manager.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::Glue::Time {
12
13AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource)
14 : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{
15 steady_clock_resource} {}
16
17AlarmWorker::~AlarmWorker() {
18 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
19
20 m_ctx.CloseEvent(m_timer_event);
21}
22
23void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
24 m_time_m = std::move(time_m);
25
26 m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
27 m_timer_timing_event = Core::Timing::CreateEvent(
28 "Glue:AlarmWorker::AlarmTimer",
29 [this](s64 time,
30 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
31 m_timer_event->Signal();
32 return std::nullopt;
33 });
34
35 AttachToClosestAlarmEvent();
36}
37
38bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info,
39 s64& out_time) {
40 bool is_valid{};
41 Service::PSC::Time::AlarmInfo alarm_info{};
42 s64 closest_time{};
43
44 auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time);
45 ASSERT(res == ResultSuccess);
46
47 if (is_valid) {
48 out_alarm_info = alarm_info;
49 out_time = closest_time;
50 }
51
52 return is_valid;
53}
54
55void AlarmWorker::OnPowerStateChanged() {
56 Service::PSC::Time::AlarmInfo closest_alarm_info{};
57 s64 closest_time{};
58 if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) {
59 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
60 m_timer_event->Clear();
61 return;
62 }
63
64 if (closest_alarm_info.alert_time <= closest_time) {
65 m_time_m->CheckAndSignalAlarms();
66 } else {
67 auto next_time{closest_alarm_info.alert_time - closest_time};
68
69 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
70 m_timer_event->Clear();
71
72 m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time),
73 m_timer_timing_event);
74 }
75}
76
77Result AlarmWorker::AttachToClosestAlarmEvent() {
78 m_time_m->GetClosestAlarmUpdatedEvent(&m_event);
79 R_SUCCEED();
80}
81
82} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/alarm_worker.h b/src/core/hle/service/glue/time/alarm_worker.h
new file mode 100644
index 000000000..f269cffdb
--- /dev/null
+++ b/src/core/hle/service/glue/time/alarm_worker.h
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/kernel_helpers.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16class ServiceManager;
17}
18
19namespace Service::Glue::Time {
20class StandardSteadyClockResource;
21
22class AlarmWorker {
23public:
24 explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource);
25 ~AlarmWorker();
26
27 void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m);
28
29 Kernel::KEvent& GetEvent() {
30 return *m_event;
31 }
32
33 Kernel::KEvent& GetTimerEvent() {
34 return *m_timer_event;
35 }
36
37 void OnPowerStateChanged();
38
39private:
40 bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time);
41 Result AttachToClosestAlarmEvent();
42
43 Core::System& m_system;
44 KernelHelpers::ServiceContext m_ctx;
45 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
46
47 Kernel::KEvent* m_event{};
48 Kernel::KEvent* m_timer_event{};
49 std::shared_ptr<Core::Timing::EventType> m_timer_timing_event;
50 StandardSteadyClockResource& m_steady_clock_resource;
51};
52
53} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.cpp b/src/core/hle/service/glue/time/file_timestamp_worker.cpp
new file mode 100644
index 000000000..5a6309549
--- /dev/null
+++ b/src/core/hle/service/glue/time/file_timestamp_worker.cpp
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/glue/time/file_timestamp_worker.h"
5#include "core/hle/service/psc/time/common.h"
6#include "core/hle/service/psc/time/system_clock.h"
7#include "core/hle/service/psc/time/time_zone_service.h"
8
9namespace Service::Glue::Time {
10
11void FileTimestampWorker::SetFilesystemPosixTime() {
12 s64 time{};
13 Service::PSC::Time::CalendarTime calendar_time{};
14 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
15
16 if (m_initialized && m_system_clock->GetCurrentTime(time) == ResultSuccess &&
17 m_time_zone->ToCalendarTimeWithMyRule(calendar_time, additional_info, time) ==
18 ResultSuccess) {
19 // TODO IFileSystemProxy::SetCurrentPosixTime
20 }
21}
22
23} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.h b/src/core/hle/service/glue/time/file_timestamp_worker.h
new file mode 100644
index 000000000..5f8b9b049
--- /dev/null
+++ b/src/core/hle/service/glue/time/file_timestamp_worker.h
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Service::PSC::Time {
11class SystemClock;
12class TimeZoneService;
13} // namespace Service::PSC::Time
14
15namespace Service::Glue::Time {
16
17class FileTimestampWorker {
18public:
19 FileTimestampWorker() = default;
20
21 void SetFilesystemPosixTime();
22
23 std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{};
24 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{};
25 bool m_initialized{};
26};
27
28} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp
new file mode 100644
index 000000000..6423e5089
--- /dev/null
+++ b/src/core/hle/service/glue/time/manager.cpp
@@ -0,0 +1,277 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8
9#include "common/settings.h"
10#include "common/time_zone.h"
11#include "core/file_sys/vfs.h"
12#include "core/hle/kernel/svc.h"
13#include "core/hle/service/glue/time/manager.h"
14#include "core/hle/service/glue/time/time_zone_binary.h"
15#include "core/hle/service/psc/time/service_manager.h"
16#include "core/hle/service/psc/time/static.h"
17#include "core/hle/service/psc/time/system_clock.h"
18#include "core/hle/service/psc/time/time_zone_service.h"
19#include "core/hle/service/set/system_settings_server.h"
20#include "core/hle/service/sm/sm.h"
21
22namespace Service::Glue::Time {
23namespace {
24
25template <typename T>
26T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
27 const char* category, const char* name) {
28 std::vector<u8> interval_buf;
29 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
30 ASSERT(res == ResultSuccess);
31
32 T v{};
33 std::memcpy(&v, interval_buf.data(), sizeof(T));
34 return v;
35}
36
37s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
38 constexpr auto is_leap = [](s32 year) -> bool {
39 return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
40 };
41 constexpr std::array<s32, 12> MonthStartDayOfYear{
42 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
43 };
44
45 s16 month_s16{calendar.month};
46 s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) +
47 ((month_s16 * 43) >> 9))};
48 s8 month_index{static_cast<s8>(calendar.month - 12 * month)};
49 if (month_index == 0) {
50 month_index = 12;
51 }
52 s32 year{(month + calendar.year) - !month_index};
53 s32 v8{year >= 0 ? year : year + 3};
54
55 s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1];
56 days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365;
57
58 if (month_index <= 2 && is_leap(year)) {
59 days_since_epoch--;
60 }
61 auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll +
62 calendar.second};
63 return epoch_s - 62135683200ll;
64}
65
66s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) {
67 Service::PSC::Time::CalendarTime calendar{
68 .year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"),
69 .month = 1,
70 .day = 1,
71 .hour = 0,
72 .minute = 0,
73 .second = 0,
74 };
75 return CalendarTimeToEpoch(calendar);
76}
77
78Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) {
79 auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
80
81 Service::PSC::Time::LocationName configured_name{};
82 std::memcpy(configured_name.name.data(), configured_zone.data(),
83 std::min(configured_name.name.size(), configured_zone.size()));
84
85 if (!IsTimeZoneBinaryValid(configured_name)) {
86 configured_zone = Common::TimeZone::FindSystemTimeZone();
87 configured_name = {};
88 std::memcpy(configured_name.name.data(), configured_zone.data(),
89 std::min(configured_name.name.size(), configured_zone.size()));
90 }
91
92 ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!",
93 configured_name.name.data());
94
95 return configured_name;
96}
97
98} // namespace
99
100TimeManager::TimeManager(Core::System& system)
101 : m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource,
102 m_file_timestamp_worker} {
103 m_time_m =
104 system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
105
106 auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm);
107 ASSERT(res == ResultSuccess);
108
109 m_set_sys =
110 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
111
112 res = MountTimeZoneBinary(system);
113 ASSERT(res == ResultSuccess);
114
115 m_worker.Initialize(m_time_sm, m_set_sys);
116
117 res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock);
118 ASSERT(res == ResultSuccess);
119
120 res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone);
121 ASSERT(res == ResultSuccess);
122
123 res = SetupStandardSteadyClockCore();
124 ASSERT(res == ResultSuccess);
125
126 Service::PSC::Time::SystemClockContext user_clock_context{};
127 res = m_set_sys->GetUserSystemClockContext(user_clock_context);
128 ASSERT(res == ResultSuccess);
129
130 // TODO the local clock should initialise with this epoch time, and be updated somewhere else on
131 // first boot to update it, but I haven't been able to find that point (likely via ntc's auto
132 // correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for
133 // first boot, grab the current real seconds.
134 auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)};
135 if (user_clock_context == Service::PSC::Time::SystemClockContext{}) {
136 m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time);
137 }
138
139 res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time);
140 ASSERT(res == ResultSuccess);
141
142 Service::PSC::Time::SystemClockContext network_clock_context{};
143 res = m_set_sys->GetNetworkSystemClockContext(network_clock_context);
144 ASSERT(res == ResultSuccess);
145
146 auto network_accuracy_m{GetSettingsItemValue<s32>(
147 m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")};
148 auto one_minute_ns{
149 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
150 s64 network_accuracy_ns{network_accuracy_m * one_minute_ns};
151
152 res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns);
153 ASSERT(res == ResultSuccess);
154
155 bool is_automatic_correction_enabled{};
156 res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled);
157 ASSERT(res == ResultSuccess);
158
159 Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{};
160 res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime(
161 automatic_correction_time_point);
162 ASSERT(res == ResultSuccess);
163
164 res = m_time_m->SetupStandardUserSystemClockCore(automatic_correction_time_point,
165 is_automatic_correction_enabled);
166 ASSERT(res == ResultSuccess);
167
168 res = m_time_m->SetupEphemeralNetworkSystemClockCore();
169 ASSERT(res == ResultSuccess);
170
171 res = SetupTimeZoneServiceCore();
172 ASSERT(res == ResultSuccess);
173
174 s64 rtc_time_s{};
175 res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s);
176 ASSERT(res == ResultSuccess);
177
178 // TODO system report "launch"
179 // "rtc_reset" = m_steady_clock_resource.m_rtc_reset
180 // "rtc_value" = rtc_time_s
181
182 m_worker.StartThread();
183
184 m_file_timestamp_worker.m_initialized = true;
185
186 s64 system_clock_time{};
187 if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(system_clock_time) ==
188 ResultSuccess) {
189 Service::PSC::Time::CalendarTime calendar_time{};
190 Service::PSC::Time::CalendarAdditionalInfo calendar_additional{};
191 if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule(
192 calendar_time, calendar_additional, system_clock_time) == ResultSuccess) {
193 // TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time,
194 // calendar_additional.ut_offset)
195 }
196 }
197}
198
199Result TimeManager::SetupStandardSteadyClockCore() {
200 Common::UUID external_clock_source_id{};
201 auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id);
202 ASSERT(res == ResultSuccess);
203
204 s64 external_steady_clock_internal_offset_s{};
205 res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s);
206 ASSERT(res == ResultSuccess);
207
208 auto one_second_ns{
209 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
210 s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s *
211 one_second_ns};
212
213 s32 standard_steady_clock_test_offset_m{
214 GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")};
215 auto one_minute_ns{
216 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
217 s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns};
218
219 auto reset_detected = m_steady_clock_resource.GetResetDetected();
220 if (reset_detected) {
221 external_clock_source_id = {};
222 }
223
224 Common::UUID clock_source_id{};
225 m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id);
226
227 if (clock_source_id != external_clock_source_id) {
228 m_set_sys->SetExternalSteadyClockSourceId(clock_source_id);
229 }
230
231 res = m_time_m->SetupStandardSteadyClockCore(clock_source_id, m_steady_clock_resource.GetTime(),
232 external_steady_clock_internal_offset_ns,
233 standard_steady_clock_test_offset_ns,
234 reset_detected);
235 ASSERT(res == ResultSuccess);
236 R_SUCCEED();
237}
238
239Result TimeManager::SetupTimeZoneServiceCore() {
240 Service::PSC::Time::LocationName name{};
241 auto res = m_set_sys->GetDeviceTimeZoneLocationName(name);
242 ASSERT(res == ResultSuccess);
243
244 auto configured_zone = GetTimeZoneString(name);
245
246 if (configured_zone.name != name.name) {
247 m_set_sys->SetDeviceTimeZoneLocationName(configured_zone);
248 name = configured_zone;
249
250 std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
251 m_time_sm->GetStandardLocalSystemClock(local_clock);
252 Service::PSC::Time::SystemClockContext context{};
253 local_clock->GetSystemClockContext(context);
254 m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point);
255 }
256
257 Service::PSC::Time::SteadyClockTimePoint time_point{};
258 res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point);
259 ASSERT(res == ResultSuccess);
260
261 auto location_count = GetTimeZoneCount();
262 Service::PSC::Time::RuleVersion rule_version{};
263 GetTimeZoneVersion(rule_version);
264
265 std::span<const u8> rule_buffer{};
266 size_t rule_size{};
267 res = GetTimeZoneRule(rule_buffer, rule_size, name);
268 ASSERT(res == ResultSuccess);
269
270 res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count,
271 rule_buffer);
272 ASSERT(res == ResultSuccess);
273
274 R_SUCCEED();
275}
276
277} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h
new file mode 100644
index 000000000..a46ec6364
--- /dev/null
+++ b/src/core/hle/service/glue/time/manager.h
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include <string>
8
9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h"
11#include "core/hle/service/glue/time/file_timestamp_worker.h"
12#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
13#include "core/hle/service/glue/time/worker.h"
14#include "core/hle/service/service.h"
15
16namespace Core {
17class System;
18}
19
20namespace Service::PSC::Time {
21class ServiceManager;
22class StaticService;
23} // namespace Service::PSC::Time
24
25namespace Service::Glue::Time {
26class TimeManager {
27public:
28 explicit TimeManager(Core::System& system);
29
30 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
31
32 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{};
33 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{};
34 StandardSteadyClockResource m_steady_clock_resource;
35 FileTimestampWorker m_file_timestamp_worker;
36 TimeWorker m_worker;
37
38private:
39 Result SetupStandardSteadyClockCore();
40 Result SetupTimeZoneServiceCore();
41};
42} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.cpp b/src/core/hle/service/glue/time/pm_state_change_handler.cpp
new file mode 100644
index 000000000..7470fb225
--- /dev/null
+++ b/src/core/hle/service/glue/time/pm_state_change_handler.cpp
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/glue/time/pm_state_change_handler.h"
5
6namespace Service::Glue::Time {
7
8PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker)
9 : m_alarm_worker{alarm_worker} {
10 // TODO Initialize IPmModule, dependent on Rtc and Fs
11}
12
13} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.h b/src/core/hle/service/glue/time/pm_state_change_handler.h
new file mode 100644
index 000000000..27d9f7872
--- /dev/null
+++ b/src/core/hle/service/glue/time/pm_state_change_handler.h
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::Glue::Time {
9class AlarmWorker;
10
11class PmStateChangeHandler {
12public:
13 explicit PmStateChangeHandler(AlarmWorker& alarm_worker);
14
15 AlarmWorker& m_alarm_worker;
16 s32 m_priority{};
17};
18} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp
new file mode 100644
index 000000000..5ebaa33e0
--- /dev/null
+++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp
@@ -0,0 +1,123 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "common/settings.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/hle/kernel/svc.h"
10#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
11#include "core/hle/service/psc/time/errors.h"
12
13namespace Service::Glue::Time {
14namespace {
15[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001;
16[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001;
17
18Result GetTimeInSeconds(Core::System& system, s64& out_time_s) {
19 out_time_s = std::chrono::duration_cast<std::chrono::seconds>(
20 std::chrono::system_clock::now().time_since_epoch())
21 .count();
22
23 if (Settings::values.custom_rtc_enabled) {
24 out_time_s += Settings::values.custom_rtc_offset.GetValue();
25 }
26 R_SUCCEED();
27}
28} // namespace
29
30StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {}
31
32void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id,
33 Common::UUID* external_source_id) {
34 constexpr size_t NUM_TRIES{20};
35
36 size_t i{0};
37 Result res{ResultSuccess};
38 for (; i < NUM_TRIES; i++) {
39 res = SetCurrentTime();
40 if (res == ResultSuccess) {
41 break;
42 }
43 Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
44 std::chrono::milliseconds(1))
45 .count());
46 }
47
48 if (i < NUM_TRIES) {
49 m_set_time_result = ResultSuccess;
50 if (*external_source_id != Service::PSC::Time::ClockSourceId{}) {
51 m_clock_source_id = *external_source_id;
52 } else {
53 m_clock_source_id = Common::UUID::MakeRandom();
54 }
55 } else {
56 m_set_time_result = res;
57 auto ticks{m_system.CoreTiming().GetClockTicks()};
58 m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count();
59 m_clock_source_id = Common::UUID::MakeRandom();
60 }
61
62 if (out_source_id) {
63 *out_source_id = m_clock_source_id;
64 }
65}
66
67bool StandardSteadyClockResource::GetResetDetected() {
68 // TODO:
69 // call Rtc::GetRtcResetDetected(Max77620RtcSession)
70 // if detected:
71 // SetSys::SetExternalSteadyClockSourceId(invalid_id)
72 // Rtc::ClearRtcResetDetected(Max77620RtcSession)
73 // set m_rtc_reset to result
74 // Instead, only set reset to true if we're booting for the first time.
75 m_rtc_reset = false;
76 return m_rtc_reset;
77}
78
79Result StandardSteadyClockResource::SetCurrentTime() {
80 auto start_tick{m_system.CoreTiming().GetClockTicks()};
81
82 s64 rtc_time_s{};
83 // TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession))
84 R_TRY(GetTimeInSeconds(m_system, rtc_time_s));
85
86 auto end_tick{m_system.CoreTiming().GetClockTicks()};
87 auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)};
88 // Why is this here?
89 R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout);
90
91 auto one_second_ns{
92 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
93 s64 boot_time{rtc_time_s * one_second_ns -
94 Service::PSC::Time::ConvertToTimeSpan(end_tick).count()};
95
96 std::scoped_lock l{m_mutex};
97 m_time = boot_time;
98 R_SUCCEED();
99}
100
101Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) {
102 // TODO
103 // R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession)
104 R_RETURN(GetTimeInSeconds(m_system, out_time));
105}
106
107void StandardSteadyClockResource::UpdateTime() {
108 constexpr size_t NUM_TRIES{3};
109
110 size_t i{0};
111 Result res{ResultSuccess};
112 for (; i < NUM_TRIES; i++) {
113 res = SetCurrentTime();
114 if (res == ResultSuccess) {
115 break;
116 }
117 Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
118 std::chrono::milliseconds(1))
119 .count());
120 }
121}
122
123} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.h b/src/core/hle/service/glue/time/standard_steady_clock_resource.h
new file mode 100644
index 000000000..978d6b63b
--- /dev/null
+++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "common/common_types.h"
9#include "core/hle/result.h"
10#include "core/hle/service/psc/time/common.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Glue::Time {
17class StandardSteadyClockResource {
18public:
19 StandardSteadyClockResource(Core::System& system);
20
21 void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id);
22
23 s64 GetTime() const {
24 return m_time;
25 }
26
27 bool GetResetDetected();
28 Result SetCurrentTime();
29 Result GetRtcTimeInSeconds(s64& out_time);
30 void UpdateTime();
31
32private:
33 Core::System& m_system;
34
35 std::mutex m_mutex;
36 Service::PSC::Time::ClockSourceId m_clock_source_id{};
37 s64 m_time{};
38 Result m_set_time_result;
39 bool m_rtc_reset;
40};
41} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp
new file mode 100644
index 000000000..63b7d91da
--- /dev/null
+++ b/src/core/hle/service/glue/time/static.cpp
@@ -0,0 +1,448 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/service/glue/time/file_timestamp_worker.h"
10#include "core/hle/service/glue/time/static.h"
11#include "core/hle/service/psc/time/errors.h"
12#include "core/hle/service/psc/time/service_manager.h"
13#include "core/hle/service/psc/time/static.h"
14#include "core/hle/service/psc/time/steady_clock.h"
15#include "core/hle/service/psc/time/system_clock.h"
16#include "core/hle/service/psc/time/time_zone_service.h"
17#include "core/hle/service/set/system_settings_server.h"
18#include "core/hle/service/sm/sm.h"
19
20namespace Service::Glue::Time {
21namespace {
22template <typename T>
23T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
24 const char* category, const char* name) {
25 std::vector<u8> interval_buf;
26 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
27 ASSERT(res == ResultSuccess);
28
29 T v{};
30 std::memcpy(&v, interval_buf.data(), sizeof(T));
31 return v;
32}
33} // namespace
34
35StaticService::StaticService(Core::System& system_,
36 Service::PSC::Time::StaticServiceSetupInfo setup_info,
37 std::shared_ptr<TimeManager> time, const char* name)
38 : ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m},
39 m_setup_info{setup_info}, m_time_sm{time->m_time_sm},
40 m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{
41 time->m_steady_clock_resource} {
42 // clang-format off
43 static const FunctionInfo functions[] = {
44 {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
45 {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
46 {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
47 {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
48 {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
49 {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
50 {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
51 {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
52 {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
53 {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
54 {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
55 {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
56 {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
57 {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
58 {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
59 {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
60 {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
61 {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
62 {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
63 };
64 // clang-format on
65
66 RegisterHandlers(functions);
67
68 m_set_sys =
69 m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
70
71 if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock &&
72 !m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location &&
73 !m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
74 m_time_m->GetStaticServiceAsAdmin(m_wrapped_service);
75 } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
76 !m_setup_info.can_write_network_clock &&
77 !m_setup_info.can_write_timezone_device_location &&
78 !m_setup_info.can_write_steady_clock &&
79 !m_setup_info.can_write_uninitialized_clock) {
80 m_time_m->GetStaticServiceAsUser(m_wrapped_service);
81 } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
82 !m_setup_info.can_write_network_clock &&
83 !m_setup_info.can_write_timezone_device_location &&
84 m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
85 m_time_m->GetStaticServiceAsRepair(m_wrapped_service);
86 } else {
87 UNREACHABLE();
88 }
89
90 auto res = m_wrapped_service->GetTimeZoneService(m_time_zone);
91 ASSERT(res == ResultSuccess);
92}
93
94void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
95 LOG_DEBUG(Service_Time, "called.");
96
97 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
98 auto res = GetStandardUserSystemClock(service);
99
100 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
101 rb.Push(res);
102 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
103}
104
105void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
106 LOG_DEBUG(Service_Time, "called.");
107
108 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
109 auto res = GetStandardNetworkSystemClock(service);
110
111 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
112 rb.Push(res);
113 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
114}
115
116void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
117 LOG_DEBUG(Service_Time, "called.");
118
119 std::shared_ptr<Service::PSC::Time::SteadyClock> service{};
120 auto res = GetStandardSteadyClock(service);
121
122 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
123 rb.Push(res);
124 rb.PushIpcInterface(std::move(service));
125}
126
127void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
128 LOG_DEBUG(Service_Time, "called.");
129
130 std::shared_ptr<TimeZoneService> service{};
131 auto res = GetTimeZoneService(service);
132
133 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
134 rb.Push(res);
135 rb.PushIpcInterface(std::move(service));
136}
137
138void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
139 LOG_DEBUG(Service_Time, "called.");
140
141 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
142 auto res = GetStandardLocalSystemClock(service);
143
144 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
145 rb.Push(res);
146 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
147}
148
149void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
150 LOG_DEBUG(Service_Time, "called.");
151
152 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
153 auto res = GetEphemeralNetworkSystemClock(service);
154
155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
156 rb.Push(res);
157 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
158}
159
160void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
161 LOG_DEBUG(Service_Time, "called.");
162
163 Kernel::KSharedMemory* shared_memory{};
164 auto res = GetSharedMemoryNativeHandle(&shared_memory);
165
166 IPC::ResponseBuilder rb{ctx, 2, 1};
167 rb.Push(res);
168 rb.PushCopyObjects(shared_memory);
169}
170
171void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
172 LOG_DEBUG(Service_Time, "called.");
173
174 IPC::RequestParser rp{ctx};
175 auto offset_ns{rp.Pop<s64>()};
176
177 auto res = SetStandardSteadyClockInternalOffset(offset_ns);
178
179 IPC::ResponseBuilder rb{ctx, 2};
180 rb.Push(res);
181}
182
183void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
184 LOG_DEBUG(Service_Time, "called.");
185
186 s64 rtc_value{};
187 auto res = GetStandardSteadyClockRtcValue(rtc_value);
188
189 IPC::ResponseBuilder rb{ctx, 4};
190 rb.Push(res);
191 rb.Push(rtc_value);
192}
193
194void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
195 HLERequestContext& ctx) {
196 LOG_DEBUG(Service_Time, "called.");
197
198 bool is_enabled{};
199 auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
200
201 IPC::ResponseBuilder rb{ctx, 3};
202 rb.Push(res);
203 rb.Push<bool>(is_enabled);
204}
205
206void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
207 HLERequestContext& ctx) {
208 LOG_DEBUG(Service_Time, "called.");
209
210 IPC::RequestParser rp{ctx};
211 auto automatic_correction{rp.Pop<bool>()};
212
213 auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
214
215 IPC::ResponseBuilder rb{ctx, 2};
216 rb.Push(res);
217}
218
219void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
220 LOG_DEBUG(Service_Time, "called.");
221
222 s32 initial_year{};
223 auto res = GetStandardUserSystemClockInitialYear(initial_year);
224
225 IPC::ResponseBuilder rb{ctx, 3};
226 rb.Push(res);
227 rb.Push(initial_year);
228}
229
230void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
231 LOG_DEBUG(Service_Time, "called.");
232
233 bool is_sufficient{};
234 auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
235
236 IPC::ResponseBuilder rb{ctx, 3};
237 rb.Push(res);
238 rb.Push<bool>(is_sufficient);
239}
240
241void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
242 HLERequestContext& ctx) {
243 LOG_DEBUG(Service_Time, "called.");
244
245 Service::PSC::Time::SteadyClockTimePoint time_point{};
246 auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
247
248 IPC::ResponseBuilder rb{ctx,
249 2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)};
250 rb.Push(res);
251 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
252}
253
254void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
255 LOG_DEBUG(Service_Time, "called.");
256
257 IPC::RequestParser rp{ctx};
258 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
259
260 s64 time{};
261 auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
262
263 IPC::ResponseBuilder rb{ctx, 4};
264 rb.Push(res);
265 rb.Push<s64>(time);
266}
267
268void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
269 LOG_DEBUG(Service_Time, "called.");
270
271 IPC::RequestParser rp{ctx};
272 auto type{rp.PopEnum<Service::PSC::Time::TimeType>()};
273
274 Service::PSC::Time::ClockSnapshot snapshot{};
275 auto res = GetClockSnapshot(snapshot, type);
276
277 ctx.WriteBuffer(snapshot);
278
279 IPC::ResponseBuilder rb{ctx, 2};
280 rb.Push(res);
281}
282
283void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
284 LOG_DEBUG(Service_Time, "called.");
285
286 IPC::RequestParser rp{ctx};
287 auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()};
288 [[maybe_unused]] auto alignment{rp.Pop<u32>()};
289 auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
290 auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
291
292 Service::PSC::Time::ClockSnapshot snapshot{};
293 auto res =
294 GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
295
296 ctx.WriteBuffer(snapshot);
297
298 IPC::ResponseBuilder rb{ctx, 2};
299 rb.Push(res);
300}
301
302void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
303 HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called.");
305
306 Service::PSC::Time::ClockSnapshot a{};
307 Service::PSC::Time::ClockSnapshot b{};
308
309 auto a_buffer{ctx.ReadBuffer(0)};
310 auto b_buffer{ctx.ReadBuffer(1)};
311
312 std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
313 std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
314
315 s64 difference{};
316 auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
317
318 IPC::ResponseBuilder rb{ctx, 4};
319 rb.Push(res);
320 rb.Push(difference);
321}
322
323void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
324 LOG_DEBUG(Service_Time, "called.");
325
326 Service::PSC::Time::ClockSnapshot a{};
327 Service::PSC::Time::ClockSnapshot b{};
328
329 auto a_buffer{ctx.ReadBuffer(0)};
330 auto b_buffer{ctx.ReadBuffer(1)};
331
332 std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
333 std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
334
335 s64 time{};
336 auto res = CalculateSpanBetween(time, a, b);
337
338 IPC::ResponseBuilder rb{ctx, 4};
339 rb.Push(res);
340 rb.Push(time);
341}
342
343// =============================== Implementations ===========================
344
345Result StaticService::GetStandardUserSystemClock(
346 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
347 R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service));
348}
349
350Result StaticService::GetStandardNetworkSystemClock(
351 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
352 R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service));
353}
354
355Result StaticService::GetStandardSteadyClock(
356 std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) {
357 R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service));
358}
359
360Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
361 out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker,
362 m_setup_info.can_write_timezone_device_location,
363 m_time_zone);
364 R_SUCCEED();
365}
366
367Result StaticService::GetStandardLocalSystemClock(
368 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
369 R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service));
370}
371
372Result StaticService::GetEphemeralNetworkSystemClock(
373 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
374 R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service));
375}
376
377Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
378 R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory));
379}
380
381Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
382 R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied);
383
384 R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset(
385 offset_ns /
386 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()));
387}
388
389Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) {
390 R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value));
391}
392
393Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
394 bool& out_automatic_correction) {
395 R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
396 out_automatic_correction));
397}
398
399Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
400 bool automatic_correction) {
401 R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled(
402 automatic_correction));
403}
404
405Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) {
406 out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year");
407 R_SUCCEED();
408}
409
410Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
411 R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
412}
413
414Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
415 Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
416 R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
417 out_time_point));
418}
419
420Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
421 s64& out_time, Service::PSC::Time::SystemClockContext& context) {
422 R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
423}
424
425Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
426 Service::PSC::Time::TimeType type) {
427 R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
428}
429
430Result StaticService::GetClockSnapshotFromSystemClockContext(
431 Service::PSC::Time::ClockSnapshot& out_snapshot,
432 Service::PSC::Time::SystemClockContext& user_context,
433 Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) {
434 R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context,
435 network_context, type));
436}
437
438Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(
439 s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) {
440 R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
441}
442
443Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
444 Service::PSC::Time::ClockSnapshot& b) {
445 R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
446}
447
448} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h
new file mode 100644
index 000000000..75fe4e2cd
--- /dev/null
+++ b/src/core/hle/service/glue/time/static.h
@@ -0,0 +1,110 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/service/glue/time/manager.h"
8#include "core/hle/service/glue/time/time_zone.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::Set {
16class ISystemSettingsServer;
17}
18
19namespace Service::PSC::Time {
20class StaticService;
21class SystemClock;
22class SteadyClock;
23class TimeZoneService;
24class ServiceManager;
25} // namespace Service::PSC::Time
26
27namespace Service::Glue::Time {
28class FileTimestampWorker;
29class StandardSteadyClockResource;
30
31class StaticService final : public ServiceFramework<StaticService> {
32public:
33 explicit StaticService(Core::System& system,
34 Service::PSC::Time::StaticServiceSetupInfo setup_info,
35 std::shared_ptr<TimeManager> time, const char* name);
36
37 ~StaticService() override = default;
38
39 Result GetStandardUserSystemClock(
40 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
41 Result GetStandardNetworkSystemClock(
42 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
43 Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service);
44 Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
45 Result GetStandardLocalSystemClock(
46 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
47 Result GetEphemeralNetworkSystemClock(
48 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
49 Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
50 Result SetStandardSteadyClockInternalOffset(s64 offset);
51 Result GetStandardSteadyClockRtcValue(s64& out_rtc_value);
52 Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction);
53 Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
54 Result GetStandardUserSystemClockInitialYear(s32& out_year);
55 Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
56 Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
57 Service::PSC::Time::SteadyClockTimePoint& out_time_point);
58 Result CalculateMonotonicSystemClockBaseTimePoint(
59 s64& out_time, Service::PSC::Time::SystemClockContext& context);
60 Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
61 Service::PSC::Time::TimeType type);
62 Result GetClockSnapshotFromSystemClockContext(
63 Service::PSC::Time::ClockSnapshot& out_snapshot,
64 Service::PSC::Time::SystemClockContext& user_context,
65 Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type);
66 Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
67 Service::PSC::Time::ClockSnapshot& a,
68 Service::PSC::Time::ClockSnapshot& b);
69 Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
70 Service::PSC::Time::ClockSnapshot& b);
71
72private:
73 Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot,
74 Service::PSC::Time::SystemClockContext& user_context,
75 Service::PSC::Time::SystemClockContext& network_context,
76 Service::PSC::Time::TimeType type);
77
78 void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
79 void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
80 void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
81 void Handle_GetTimeZoneService(HLERequestContext& ctx);
82 void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
83 void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
84 void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
85 void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
86 void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
87 void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
88 void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
89 void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
90 void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
91 void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
92 void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
93 void Handle_GetClockSnapshot(HLERequestContext& ctx);
94 void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
95 void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
96 void Handle_CalculateSpanBetween(HLERequestContext& ctx);
97
98 Core::System& m_system;
99
100 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
101 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
102 std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service;
103
104 Service::PSC::Time::StaticServiceSetupInfo m_setup_info;
105 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
106 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone;
107 FileTimestampWorker& m_file_timestamp_worker;
108 StandardSteadyClockResource& m_standard_steady_clock_resource;
109};
110} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp
new file mode 100644
index 000000000..503c327dd
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone.cpp
@@ -0,0 +1,377 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/service/glue/time/file_timestamp_worker.h"
9#include "core/hle/service/glue/time/time_zone.h"
10#include "core/hle/service/glue/time/time_zone_binary.h"
11#include "core/hle/service/psc/time/time_zone_service.h"
12#include "core/hle/service/set/system_settings_server.h"
13#include "core/hle/service/sm/sm.h"
14
15namespace Service::Glue::Time {
16namespace {
17static std::mutex g_list_mutex;
18static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{};
19} // namespace
20
21TimeZoneService::TimeZoneService(
22 Core::System& system_, FileTimestampWorker& file_timestamp_worker,
23 bool can_write_timezone_device_location,
24 std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
25 : ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
26 m_can_write_timezone_device_location{can_write_timezone_device_location},
27 m_file_timestamp_worker{file_timestamp_worker},
28 m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} {
29 // clang-format off
30 static const FunctionInfo functions[] = {
31 {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
32 {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
33 {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
34 {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
35 {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
36 {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
37 {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
38 {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
39 {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
40 {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
41 {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
42 {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
43 {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
44 {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
45 };
46 // clang-format on
47 RegisterHandlers(functions);
48
49 g_list_nodes.clear();
50 m_set_sys =
51 m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
52}
53
54TimeZoneService::~TimeZoneService() = default;
55
56void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
57 LOG_DEBUG(Service_Time, "called.");
58
59 Service::PSC::Time::LocationName name{};
60 auto res = GetDeviceLocationName(name);
61
62 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
63 rb.Push(res);
64 rb.PushRaw<Service::PSC::Time::LocationName>(name);
65}
66
67void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
68 LOG_DEBUG(Service_Time, "called.");
69
70 IPC::RequestParser rp{ctx};
71 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
72
73 auto res = SetDeviceLocation(name);
74
75 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(res);
77}
78
79void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
80 LOG_DEBUG(Service_Time, "called.");
81
82 u32 count{};
83 auto res = GetTotalLocationNameCount(count);
84
85 IPC::ResponseBuilder rb{ctx, 3};
86 rb.Push(res);
87 rb.Push(count);
88}
89
90void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
91 LOG_DEBUG(Service_Time, "called.");
92
93 IPC::RequestParser rp{ctx};
94 auto index{rp.Pop<u32>()};
95
96 auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)};
97
98 std::vector<Service::PSC::Time::LocationName> names{};
99 u32 count{};
100 auto res = LoadLocationNameList(count, names, max_names, index);
101
102 ctx.WriteBuffer(names);
103
104 IPC::ResponseBuilder rb{ctx, 3};
105 rb.Push(res);
106 rb.Push(count);
107}
108
109void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
110 LOG_DEBUG(Service_Time, "called.");
111
112 IPC::RequestParser rp{ctx};
113 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
114
115 Tz::Rule rule{};
116 auto res = LoadTimeZoneRule(rule, name);
117
118 ctx.WriteBuffer(rule);
119
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(res);
122}
123
124void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
125 LOG_DEBUG(Service_Time, "called.");
126
127 Service::PSC::Time::RuleVersion rule_version{};
128 auto res = GetTimeZoneRuleVersion(rule_version);
129
130 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)};
131 rb.Push(res);
132 rb.PushRaw<Service::PSC::Time::RuleVersion>(rule_version);
133}
134
135void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
136 LOG_DEBUG(Service_Time, "called.");
137
138 Service::PSC::Time::LocationName name{};
139 Service::PSC::Time::SteadyClockTimePoint time_point{};
140 auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
141
142 IPC::ResponseBuilder rb{ctx,
143 2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) +
144 (sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))};
145 rb.Push(res);
146 rb.PushRaw<Service::PSC::Time::LocationName>(name);
147 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
148}
149
150void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
151 LOG_DEBUG(Service_Time, "called.");
152
153 auto res = SetDeviceLocationNameWithTimeZoneRule();
154
155 IPC::ResponseBuilder rb{ctx, 2};
156 rb.Push(res);
157}
158
159void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
160 LOG_DEBUG(Service_Time, "called.");
161
162 IPC::ResponseBuilder rb{ctx, 2};
163 rb.Push(Service::PSC::Time::ResultNotImplemented);
164}
165
166void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
167 HLERequestContext& ctx) {
168 LOG_DEBUG(Service_Time, "called.");
169
170 Kernel::KEvent* event{};
171 auto res = GetDeviceLocationNameOperationEventReadableHandle(&event);
172
173 IPC::ResponseBuilder rb{ctx, 2, 1};
174 rb.Push(res);
175 rb.PushCopyObjects(event->GetReadableEvent());
176}
177
178void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
179 LOG_DEBUG(Service_Time, "called.");
180
181 IPC::RequestParser rp{ctx};
182 auto time{rp.Pop<s64>()};
183
184 auto rule_buffer{ctx.ReadBuffer()};
185 Tz::Rule rule{};
186 std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
187
188 Service::PSC::Time::CalendarTime calendar_time{};
189 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
190 auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
191
192 IPC::ResponseBuilder rb{ctx,
193 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
194 (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
195 rb.Push(res);
196 rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
197 rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
198}
199
200void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
201 IPC::RequestParser rp{ctx};
202 auto time{rp.Pop<s64>()};
203
204 LOG_DEBUG(Service_Time, "called. time={}", time);
205
206 Service::PSC::Time::CalendarTime calendar_time{};
207 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
208 auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
209
210 IPC::ResponseBuilder rb{ctx,
211 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
212 (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
213 rb.Push(res);
214 rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
215 rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
216}
217
218void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
219 IPC::RequestParser rp{ctx};
220 auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
221
222 LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}",
223 calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute,
224 calendar.second);
225
226 auto binary{ctx.ReadBuffer()};
227
228 Tz::Rule rule{};
229 std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
230
231 u32 count{};
232 std::array<s64, 2> times{};
233 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
234
235 auto res = ToPosixTime(count, times, times_count, calendar, rule);
236
237 ctx.WriteBuffer(times);
238
239 IPC::ResponseBuilder rb{ctx, 3};
240 rb.Push(res);
241 rb.Push(count);
242}
243
244void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
245 LOG_DEBUG(Service_Time, "called.");
246
247 IPC::RequestParser rp{ctx};
248 auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
249
250 u32 count{};
251 std::array<s64, 2> times{};
252 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
253
254 auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
255
256 ctx.WriteBuffer(times);
257
258 IPC::ResponseBuilder rb{ctx, 3};
259 rb.Push(res);
260 rb.Push(count);
261}
262
263// =============================== Implementations ===========================
264
265Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) {
266 R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
267}
268
269Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& location_name) {
270 R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
271 R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound);
272
273 std::scoped_lock l{m_mutex};
274
275 std::span<const u8> binary{};
276 size_t binary_size{};
277 R_TRY(GetTimeZoneRule(binary, binary_size, location_name))
278
279 R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary));
280
281 m_file_timestamp_worker.SetFilesystemPosixTime();
282
283 Service::PSC::Time::SteadyClockTimePoint time_point{};
284 Service::PSC::Time::LocationName name{};
285 R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(time_point, name));
286
287 m_set_sys->SetDeviceTimeZoneLocationName(name);
288 m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point);
289
290 std::scoped_lock m{g_list_mutex};
291 for (auto& operation_event : g_list_nodes) {
292 operation_event.m_event->Signal();
293 }
294 R_SUCCEED();
295}
296
297Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
298 R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
299}
300
301Result TimeZoneService::LoadLocationNameList(
302 u32& out_count, std::vector<Service::PSC::Time::LocationName>& out_names, size_t max_names,
303 u32 index) {
304 std::scoped_lock l{m_mutex};
305 R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index));
306}
307
308Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule,
309 Service::PSC::Time::LocationName& name) {
310 std::scoped_lock l{m_mutex};
311 std::span<const u8> binary{};
312 size_t binary_size{};
313 R_TRY(GetTimeZoneRule(binary, binary_size, name))
314 R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary));
315}
316
317Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
318 R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
319}
320
321Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
322 Service::PSC::Time::SteadyClockTimePoint& out_time_point,
323 Service::PSC::Time::LocationName& location_name) {
324 R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name));
325}
326
327Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() {
328 R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
329 R_RETURN(Service::PSC::Time::ResultNotImplemented);
330}
331
332Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
333 Kernel::KEvent** out_event) {
334 if (!operation_event_initialized) {
335 operation_event_initialized = false;
336
337 m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event);
338 m_operation_event.m_event =
339 m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent");
340 operation_event_initialized = true;
341 std::scoped_lock l{m_mutex};
342 g_list_nodes.push_back(m_operation_event);
343 }
344
345 *out_event = m_operation_event.m_event;
346 R_SUCCEED();
347}
348
349Result TimeZoneService::ToCalendarTime(
350 Service::PSC::Time::CalendarTime& out_calendar_time,
351 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) {
352 R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
353}
354
355Result TimeZoneService::ToCalendarTimeWithMyRule(
356 Service::PSC::Time::CalendarTime& out_calendar_time,
357 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) {
358 R_RETURN(
359 m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
360}
361
362Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
363 u32 out_times_count,
364 Service::PSC::Time::CalendarTime& calendar_time,
365 Tz::Rule& rule) {
366 R_RETURN(
367 m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
368}
369
370Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
371 u32 out_times_count,
372 Service::PSC::Time::CalendarTime& calendar_time) {
373 R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count,
374 calendar_time));
375}
376
377} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h
new file mode 100644
index 000000000..3c8ae4bf8
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone.h
@@ -0,0 +1,95 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <span>
9#include <vector>
10
11#include "core/hle/service/ipc_helpers.h"
12#include "core/hle/service/psc/time/common.h"
13#include "core/hle/service/server_manager.h"
14#include "core/hle/service/service.h"
15
16namespace Core {
17class System;
18}
19
20namespace Tz {
21struct Rule;
22}
23
24namespace Service::Set {
25class ISystemSettingsServer;
26}
27
28namespace Service::PSC::Time {
29class TimeZoneService;
30}
31
32namespace Service::Glue::Time {
33class FileTimestampWorker;
34
35class TimeZoneService final : public ServiceFramework<TimeZoneService> {
36public:
37 explicit TimeZoneService(
38 Core::System& system, FileTimestampWorker& file_timestamp_worker,
39 bool can_write_timezone_device_location,
40 std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service);
41
42 ~TimeZoneService() override;
43
44 Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name);
45 Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name);
46 Result GetTotalLocationNameCount(u32& out_count);
47 Result LoadLocationNameList(u32& out_count,
48 std::vector<Service::PSC::Time::LocationName>& out_names,
49 size_t max_names, u32 index);
50 Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name);
51 Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version);
52 Result GetDeviceLocationNameAndUpdatedTime(
53 Service::PSC::Time::SteadyClockTimePoint& out_time_point,
54 Service::PSC::Time::LocationName& location_name);
55 Result SetDeviceLocationNameWithTimeZoneRule();
56 Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event);
57 Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time,
58 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time,
59 Tz::Rule& rule);
60 Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time,
61 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info,
62 s64 time);
63 Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
64 Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule);
65 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
66 Service::PSC::Time::CalendarTime& calendar_time);
67
68private:
69 void Handle_GetDeviceLocationName(HLERequestContext& ctx);
70 void Handle_SetDeviceLocationName(HLERequestContext& ctx);
71 void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
72 void Handle_LoadLocationNameList(HLERequestContext& ctx);
73 void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
74 void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
75 void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
76 void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
77 void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
78 void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
79 void Handle_ToCalendarTime(HLERequestContext& ctx);
80 void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
81 void Handle_ToPosixTime(HLERequestContext& ctx);
82 void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
83
84 Core::System& m_system;
85 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
86
87 bool m_can_write_timezone_device_location;
88 FileTimestampWorker& m_file_timestamp_worker;
89 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
90 std::mutex m_mutex;
91 bool operation_event_initialized{};
92 Service::PSC::Time::OperationEvent m_operation_event;
93};
94
95} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp
new file mode 100644
index 000000000..67969aa3f
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone_binary.cpp
@@ -0,0 +1,221 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/file_sys/content_archive.h"
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/registered_cache.h"
8#include "core/file_sys/romfs.h"
9#include "core/file_sys/system_archive/system_archive.h"
10#include "core/file_sys/vfs.h"
11#include "core/hle/service/filesystem/filesystem.h"
12#include "core/hle/service/glue/time/time_zone_binary.h"
13
14namespace Service::Glue::Time {
15namespace {
16constexpr u64 TimeZoneBinaryId = 0x10000000000080E;
17
18static FileSys::VirtualDir g_time_zone_binary_romfs{};
19static Result g_time_zone_binary_mount_result{ResultUnknown};
20static std::vector<u8> g_time_zone_scratch_space(0x2800, 0);
21
22Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size,
23 std::string_view path) {
24 R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result);
25
26 auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
27 R_UNLESS(vfs_file, ResultUnknown);
28
29 auto file_size{vfs_file->GetSize()};
30 R_UNLESS(file_size > 0, ResultUnknown);
31
32 R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed);
33
34 out_read_size = vfs_file->Read(out_buffer.data(), file_size);
35 R_UNLESS(out_read_size > 0, ResultUnknown);
36
37 R_SUCCEED();
38}
39} // namespace
40
41void ResetTimeZoneBinary() {
42 g_time_zone_binary_romfs = {};
43 g_time_zone_binary_mount_result = ResultUnknown;
44 g_time_zone_scratch_space.clear();
45 g_time_zone_scratch_space.resize(0x2800, 0);
46}
47
48Result MountTimeZoneBinary(Core::System& system) {
49 ResetTimeZoneBinary();
50
51 auto& fsc{system.GetFileSystemController()};
52 std::unique_ptr<FileSys::NCA> nca{};
53
54 auto* bis_system = fsc.GetSystemNANDContents();
55
56 R_UNLESS(bis_system, ResultUnknown);
57
58 nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data);
59
60 if (nca) {
61 g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
62 }
63
64 if (g_time_zone_binary_romfs) {
65 // Validate that the romfs is readable, using invalid firmware keys can cause this to get
66 // set but the files to be garbage. In that case, we want to hit the next path and
67 // synthesise them instead.
68 Service::PSC::Time::LocationName name{"Etc/GMT"};
69 if (!IsTimeZoneBinaryValid(name)) {
70 ResetTimeZoneBinary();
71 }
72 }
73
74 if (!g_time_zone_binary_romfs) {
75 g_time_zone_binary_romfs = FileSys::ExtractRomFS(
76 FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
77 }
78
79 R_UNLESS(g_time_zone_binary_romfs, ResultUnknown);
80
81 g_time_zone_binary_mount_result = ResultSuccess;
82 R_SUCCEED();
83}
84
85void GetTimeZoneBinaryListPath(std::string& out_path) {
86 if (g_time_zone_binary_mount_result != ResultSuccess) {
87 return;
88 }
89 // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary");
90 out_path = "/binaryList.txt";
91}
92
93void GetTimeZoneBinaryVersionPath(std::string& out_path) {
94 if (g_time_zone_binary_mount_result != ResultSuccess) {
95 return;
96 }
97 // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary");
98 out_path = "/version.txt";
99}
100
101void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) {
102 if (g_time_zone_binary_mount_result != ResultSuccess) {
103 return;
104 }
105 // out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name);
106 out_path = fmt::format("/zoneinfo/{}", name.name.data());
107}
108
109bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
110 std::string path{};
111 GetTimeZoneZonePath(path, name);
112
113 auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
114 if (!vfs_file) {
115 LOG_INFO(Service_Time, "Could not find timezone file {}", path);
116 return false;
117 }
118 return vfs_file->GetSize() != 0;
119}
120
121u32 GetTimeZoneCount() {
122 std::string path{};
123 GetTimeZoneBinaryListPath(path);
124
125 size_t bytes_read{};
126 if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) {
127 return 0;
128 }
129 if (bytes_read == 0) {
130 return 0;
131 }
132
133 auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read);
134 u32 count{};
135 for (auto chr : chars) {
136 if (chr == '\n') {
137 count++;
138 }
139 }
140 return count;
141}
142
143Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
144 std::string path{};
145 GetTimeZoneBinaryVersionPath(path);
146
147 auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version),
148 sizeof(Service::PSC::Time::RuleVersion))};
149 size_t bytes_read{};
150 R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(),
151 path));
152
153 rule_version_buffer[bytes_read] = 0;
154 R_SUCCEED();
155}
156
157Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
158 Service::PSC::Time::LocationName& name) {
159 std::string path{};
160 GetTimeZoneZonePath(path, name);
161
162 size_t bytes_read{};
163 R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
164 g_time_zone_scratch_space.size(), path));
165
166 out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read);
167 out_rule_size = bytes_read;
168 R_SUCCEED();
169}
170
171Result GetTimeZoneLocationList(u32& out_count,
172 std::vector<Service::PSC::Time::LocationName>& out_names,
173 size_t max_names, u32 index) {
174 std::string path{};
175 GetTimeZoneBinaryListPath(path);
176
177 size_t bytes_read{};
178 R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
179 g_time_zone_scratch_space.size(), path));
180
181 out_count = 0;
182 R_SUCCEED_IF(bytes_read == 0);
183
184 Service::PSC::Time::LocationName current_name{};
185 size_t current_name_len{};
186 std::span<const u8> chars{g_time_zone_scratch_space};
187 u32 name_count{};
188
189 for (auto chr : chars) {
190 if (chr == '\r') {
191 continue;
192 }
193
194 if (chr == '\n') {
195 if (name_count >= index) {
196 out_names.push_back(current_name);
197 out_count++;
198 if (out_count >= max_names) {
199 break;
200 }
201 }
202 name_count++;
203 current_name_len = 0;
204 current_name = {};
205 continue;
206 }
207
208 if (chr == '\0') {
209 break;
210 }
211
212 R_UNLESS(current_name_len <= current_name.name.size() - 2,
213 Service::PSC::Time::ResultFailed);
214
215 current_name.name[current_name_len++] = chr;
216 }
217
218 R_SUCCEED();
219}
220
221} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h
new file mode 100644
index 000000000..2cad6b458
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone_binary.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <string>
8#include <string_view>
9
10#include "core/hle/service/psc/time/common.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Glue::Time {
17
18void ResetTimeZoneBinary();
19Result MountTimeZoneBinary(Core::System& system);
20void GetTimeZoneBinaryListPath(std::string& out_path);
21void GetTimeZoneBinaryVersionPath(std::string& out_path);
22void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name);
23bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name);
24u32 GetTimeZoneCount();
25Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version);
26Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
27 Service::PSC::Time::LocationName& name);
28Result GetTimeZoneLocationList(u32& out_count,
29 std::vector<Service::PSC::Time::LocationName>& out_names,
30 size_t max_names, u32 index);
31
32} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp
new file mode 100644
index 000000000..ea0e49b90
--- /dev/null
+++ b/src/core/hle/service/glue/time/worker.cpp
@@ -0,0 +1,338 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/service/glue/time/file_timestamp_worker.h"
8#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
9#include "core/hle/service/glue/time/worker.h"
10#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/psc/time/service_manager.h"
12#include "core/hle/service/psc/time/static.h"
13#include "core/hle/service/psc/time/system_clock.h"
14#include "core/hle/service/set/system_settings_server.h"
15#include "core/hle/service/sm/sm.h"
16
17namespace Service::Glue::Time {
18namespace {
19
20bool g_ig_report_network_clock_context_set{};
21Service::PSC::Time::SystemClockContext g_report_network_clock_context{};
22bool g_ig_report_ephemeral_clock_context_set{};
23Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{};
24
25template <typename T>
26T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
27 const char* category, const char* name) {
28 std::vector<u8> interval_buf;
29 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
30 ASSERT(res == ResultSuccess);
31
32 T v{};
33 std::memcpy(&v, interval_buf.data(), sizeof(T));
34 return v;
35}
36
37} // namespace
38
39TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
40 FileTimestampWorker& file_timestamp_worker)
41 : m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")},
42 m_steady_clock_resource{steady_clock_resource},
43 m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
44 "Glue:58:SteadyClockTimerEvent")},
45 m_timer_file_system{m_ctx.CreateEvent("Glue:58:FileTimeTimerEvent")},
46 m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
47 g_ig_report_network_clock_context_set = false;
48 g_report_network_clock_context = {};
49 g_ig_report_ephemeral_clock_context_set = false;
50 g_report_ephemeral_clock_context = {};
51
52 m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
53 "Time::SteadyClockEvent",
54 [this](s64 time,
55 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
56 m_timer_steady_clock->Signal();
57 return std::nullopt;
58 });
59
60 m_timer_file_system_timing_event = Core::Timing::CreateEvent(
61 "Time::SteadyClockEvent",
62 [this](s64 time,
63 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
64 m_timer_file_system->Signal();
65 return std::nullopt;
66 });
67}
68
69TimeWorker::~TimeWorker() {
70 m_local_clock_event->Signal();
71 m_network_clock_event->Signal();
72 m_ephemeral_clock_event->Signal();
73 std::this_thread::sleep_for(std::chrono::milliseconds(16));
74
75 m_thread.request_stop();
76 m_event->Signal();
77 m_thread.join();
78
79 m_ctx.CloseEvent(m_event);
80 m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
81 m_ctx.CloseEvent(m_timer_steady_clock);
82 m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event);
83 m_ctx.CloseEvent(m_timer_file_system);
84}
85
86void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
87 std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) {
88 m_set_sys = std::move(set_sys);
89 m_time_m =
90 m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
91 m_time_sm = std::move(time_sm);
92
93 m_alarm_worker.Initialize(m_time_m);
94
95 auto steady_clock_interval_m = GetSettingsItemValue<s32>(
96 m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes");
97
98 auto one_minute_ns{
99 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
100 s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns};
101
102 m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
103 std::chrono::nanoseconds(steady_clock_interval_ns),
104 m_timer_steady_clock_timing_event);
105
106 auto fs_notify_time_s =
107 GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds");
108 auto one_second_ns{
109 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
110 s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns};
111
112 m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
113 std::chrono::nanoseconds(fs_notify_time_ns),
114 m_timer_file_system_timing_event);
115
116 auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock);
117 ASSERT(res == ResultSuccess);
118 res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event);
119 ASSERT(res == ResultSuccess);
120
121 res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock);
122 ASSERT(res == ResultSuccess);
123 res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event);
124 ASSERT(res == ResultSuccess);
125
126 res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock);
127 ASSERT(res == ResultSuccess);
128 res =
129 m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event);
130 ASSERT(res == ResultSuccess);
131
132 res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
133 &m_standard_user_auto_correct_clock_event);
134 ASSERT(res == ResultSuccess);
135}
136
137void TimeWorker::StartThread() {
138 m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
139}
140
141void TimeWorker::ThreadFunc(std::stop_token stop_token) {
142 Common::SetCurrentThreadName("TimeWorker");
143 Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
144
145 enum class EventType {
146 Exit = 0,
147 IpmModuleService_GetEvent = 1,
148 PowerStateChange = 2,
149 SignalAlarms = 3,
150 UpdateLocalSystemClock = 4,
151 UpdateNetworkSystemClock = 5,
152 UpdateEphemeralSystemClock = 6,
153 UpdateSteadyClock = 7,
154 UpdateFileTimestamp = 8,
155 AutoCorrect = 9,
156 Max = 10,
157 };
158
159 s32 num_objs{};
160 std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
161 std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
162
163 const auto AddWaiter{
164 [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
165 // Open a new reference to the object.
166 synchronization_object->Open();
167
168 // Insert into the list.
169 wait_indices[num_objs] = type;
170 wait_objs[num_objs++] = synchronization_object;
171 }};
172
173 while (!stop_token.stop_requested()) {
174 SCOPE_EXIT({
175 for (s32 i = 0; i < num_objs; i++) {
176 wait_objs[i]->Close();
177 }
178 });
179
180 num_objs = {};
181 wait_objs = {};
182 if (m_pm_state_change_handler.m_priority != 0) {
183 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
184 // TODO
185 // AddWaiter(gIPmModuleService::GetEvent(), 1);
186 AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
187 } else {
188 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
189 // TODO
190 // AddWaiter(gIPmModuleService::GetEvent(), 1);
191 AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
192 AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
193 AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock);
194 AddWaiter(&m_network_clock_event->GetReadableEvent(),
195 EventType::UpdateNetworkSystemClock);
196 AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(),
197 EventType::UpdateEphemeralSystemClock);
198 AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
199 AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
200 AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(),
201 EventType::AutoCorrect);
202 }
203
204 s32 out_index{-1};
205 Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
206 num_objs, -1);
207 ASSERT(out_index >= 0 && out_index < num_objs);
208
209 if (stop_token.stop_requested()) {
210 return;
211 }
212
213 switch (wait_indices[out_index]) {
214 case EventType::Exit:
215 return;
216
217 case EventType::IpmModuleService_GetEvent:
218 // TODO
219 // IPmModuleService::GetEvent()
220 // clear the event
221 // Handle power state change event
222 break;
223
224 case EventType::PowerStateChange:
225 m_alarm_worker.GetEvent().Clear();
226 if (m_pm_state_change_handler.m_priority <= 1) {
227 m_alarm_worker.OnPowerStateChanged();
228 }
229 break;
230
231 case EventType::SignalAlarms:
232 m_alarm_worker.GetTimerEvent().Clear();
233 m_time_m->CheckAndSignalAlarms();
234 break;
235
236 case EventType::UpdateLocalSystemClock: {
237 m_local_clock_event->Clear();
238
239 Service::PSC::Time::SystemClockContext context{};
240 auto res = m_local_clock->GetSystemClockContext(context);
241 ASSERT(res == ResultSuccess);
242
243 m_set_sys->SetUserSystemClockContext(context);
244
245 m_file_timestamp_worker.SetFilesystemPosixTime();
246 } break;
247
248 case EventType::UpdateNetworkSystemClock: {
249 m_network_clock_event->Clear();
250 Service::PSC::Time::SystemClockContext context{};
251 auto res = m_network_clock->GetSystemClockContext(context);
252 ASSERT(res == ResultSuccess);
253 m_set_sys->SetNetworkSystemClockContext(context);
254
255 s64 time{};
256 if (m_network_clock->GetCurrentTime(time) != ResultSuccess) {
257 break;
258 }
259
260 [[maybe_unused]] auto offset_before{
261 g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0};
262 // TODO system report "standard_netclock_operation"
263 // "clock_time" = time
264 // "context_offset_before" = offset_before
265 // "context_offset_after" = context.offset
266 g_report_network_clock_context = context;
267 if (!g_ig_report_network_clock_context_set) {
268 g_ig_report_network_clock_context_set = true;
269 }
270
271 m_file_timestamp_worker.SetFilesystemPosixTime();
272 } break;
273
274 case EventType::UpdateEphemeralSystemClock: {
275 m_ephemeral_clock_event->Clear();
276
277 Service::PSC::Time::SystemClockContext context{};
278 auto res = m_ephemeral_clock->GetSystemClockContext(context);
279 if (res != ResultSuccess) {
280 break;
281 }
282
283 s64 time{};
284 res = m_ephemeral_clock->GetCurrentTime(time);
285 if (res != ResultSuccess) {
286 break;
287 }
288
289 [[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set
290 ? g_report_ephemeral_clock_context.offset
291 : 0};
292 // TODO system report "ephemeral_netclock_operation"
293 // "clock_time" = time
294 // "context_offset_before" = offset_before
295 // "context_offset_after" = context.offset
296 g_report_ephemeral_clock_context = context;
297 if (!g_ig_report_ephemeral_clock_context_set) {
298 g_ig_report_ephemeral_clock_context_set = true;
299 }
300 } break;
301
302 case EventType::UpdateSteadyClock:
303 m_timer_steady_clock->Clear();
304
305 m_steady_clock_resource.UpdateTime();
306 m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
307 break;
308
309 case EventType::UpdateFileTimestamp:
310 m_timer_file_system->Clear();
311
312 m_file_timestamp_worker.SetFilesystemPosixTime();
313 break;
314
315 case EventType::AutoCorrect: {
316 m_standard_user_auto_correct_clock_event->Clear();
317
318 bool automatic_correction{};
319 auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
320 automatic_correction);
321 ASSERT(res == ResultSuccess);
322
323 Service::PSC::Time::SteadyClockTimePoint time_point{};
324 res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
325 ASSERT(res == ResultSuccess);
326
327 m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
328 m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
329 } break;
330
331 default:
332 UNREACHABLE();
333 break;
334 }
335 }
336}
337
338} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/worker.h b/src/core/hle/service/glue/time/worker.h
new file mode 100644
index 000000000..adbbe6b6d
--- /dev/null
+++ b/src/core/hle/service/glue/time/worker.h
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/glue/time/alarm_worker.h"
9#include "core/hle/service/glue/time/pm_state_change_handler.h"
10#include "core/hle/service/kernel_helpers.h"
11
12namespace Service::Set {
13class ISystemSettingsServer;
14}
15
16namespace Service::PSC::Time {
17class StaticService;
18class SystemClock;
19} // namespace Service::PSC::Time
20
21namespace Service::Glue::Time {
22class FileTimestampWorker;
23class StandardSteadyClockResource;
24
25class TimeWorker {
26public:
27 explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
28 FileTimestampWorker& file_timestamp_worker);
29 ~TimeWorker();
30
31 void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
32 std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys);
33
34 void StartThread();
35
36private:
37 void ThreadFunc(std::stop_token stop_token);
38
39 Core::System& m_system;
40 KernelHelpers::ServiceContext m_ctx;
41 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
42
43 std::jthread m_thread;
44 Kernel::KEvent* m_event{};
45 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
46 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
47 std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock;
48 std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock;
49 std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock;
50 StandardSteadyClockResource& m_steady_clock_resource;
51 FileTimestampWorker& m_file_timestamp_worker;
52 Kernel::KEvent* m_local_clock_event{};
53 Kernel::KEvent* m_network_clock_event{};
54 Kernel::KEvent* m_ephemeral_clock_event{};
55 Kernel::KEvent* m_standard_user_auto_correct_clock_event{};
56 Kernel::KEvent* m_timer_steady_clock{};
57 std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event;
58 Kernel::KEvent* m_timer_file_system{};
59 std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event;
60 AlarmWorker m_alarm_worker;
61 PmStateChangeHandler m_pm_state_change_handler;
62};
63
64} // namespace Service::Glue::Time