Compare commits

..

17 Commits

Author SHA1 Message Date
Kelebek1
e4915fb7d2 Rework time service to fix time passing offline. 2024-01-24 04:26:55 +00:00
liamwhite
a560b9f5a2 Merge pull request #12678 from german77/settings_impl
service: set: Implement stubbed functions
2024-01-18 21:18:37 -05:00
liamwhite
4f04bd3697 Merge pull request #12683 from german77/amiibo-dump
service: nfc: Create backup when none exist
2024-01-18 21:18:27 -05:00
liamwhite
97c8b49444 Merge pull request #12644 from liamwhite/vkspec-image-offset
shader_recompiler: fix Offset operand usage for non-OpImage*Gather
2024-01-18 21:18:19 -05:00
Charles Lombardo
3092855d5a Merge pull request #12702 from german77/android-input
input_common: Add android input engine
2024-01-18 09:16:58 -05:00
Narr the Reg
72f803c366 input_common: Add android input engine 2024-01-17 22:47:56 -06:00
liamwhite
c87b96435d Merge pull request #12699 from t895/overlay-saving
android: Save overlay data while using emulation fragment
2024-01-17 22:56:40 -05:00
t895
116f76e4b6 android: Save overlay data while using emulation fragment
This should have been fully embraced before but the items within the popup menu and the adjust controls dialog fell through. This ensures that everything related to the overlay is saved during emulation and can't be lost during a crash.
2024-01-17 20:14:25 -05:00
liamwhite
915efa4236 Merge pull request #12689 from liamwhite/remove-format
ci: remove format dep from mainline step2
2024-01-17 00:36:07 -05:00
Liam
4548e5ae1d ci: remove format dep from mainline step2 2024-01-16 22:59:20 -05:00
Narr the Reg
46c2435235 Merge pull request #12380 from flodavid/save-profile
Save configuration profile name used by players
2024-01-16 21:27:25 -06:00
Narr the Reg
7f5adf8982 service: set: Implement stubbed functions 2024-01-15 23:17:03 -06:00
Narr the Reg
89d6856090 service: set: Refractor setting service 2024-01-15 23:16:36 -06:00
liamwhite
2c29c2b8dd Merge pull request #12686 from szepeviktor/typos3
Fix more typos
2024-01-15 23:26:08 -05:00
Narr the Reg
c661b95864 service: nfc: Create backup when none exist 2024-01-15 14:07:54 -06:00
Liam
2044a289f8 shader_recompiler: fix Offset operand usage for non-OpImage*Gather 2024-01-11 00:56:37 -05:00
flodavid
63b835f822 Save profile name used
- Save the profile name in global config
- Read the profile name when reading the global config
2024-01-08 18:43:56 +01:00
171 changed files with 9759 additions and 4588 deletions

View File

@@ -33,7 +33,6 @@ stages:
cache: 'true'
version: $(DisplayVersion)
- stage: build_win
dependsOn: format
displayName: 'build-windows'
jobs:
- job: build

View File

@@ -178,6 +178,9 @@ if (NOT TARGET stb::headers)
add_library(stb::headers ALIAS stb)
endif()
add_library(tz tz/tz/tz.cpp)
target_include_directories(tz PUBLIC ./tz)
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
target_include_directories(bc_decoder PUBLIC ./bc_decoder)

1636
externals/tz/tz/tz.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

81
externals/tz/tz/tz.h vendored Normal file
View File

@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 1996 Arthur David Olson
// SPDX-License-Identifier: BSD-2-Clause
#pragma once
#include <cstdint>
#include <limits>
#include <span>
#include <array>
#include <time.h>
namespace Tz {
using u8 = uint8_t;
using s8 = int8_t;
using u16 = uint16_t;
using s16 = int16_t;
using u32 = uint32_t;
using s32 = int32_t;
using u64 = uint64_t;
using s64 = int64_t;
constexpr size_t TZ_MAX_TIMES = 1000;
constexpr size_t TZ_MAX_TYPES = 128;
constexpr size_t TZ_MAX_CHARS = 50;
constexpr size_t MY_TZNAME_MAX = 255;
constexpr size_t TZNAME_MAXIMUM = 255;
constexpr size_t TZ_MAX_LEAPS = 50;
constexpr s64 TIME_T_MAX = std::numeric_limits<s64>::max();
constexpr s64 TIME_T_MIN = std::numeric_limits<s64>::min();
constexpr size_t CHARS_EXTRA = 3;
constexpr size_t MAX_ZONE_CHARS = std::max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof("UTC"));
constexpr size_t MAX_TZNAME_CHARS = 2 * (MY_TZNAME_MAX + 1);
struct ttinfo {
s32 tt_utoff;
bool tt_isdst;
s32 tt_desigidx;
bool tt_ttisstd;
bool tt_ttisut;
};
static_assert(sizeof(ttinfo) == 0x10, "ttinfo has the wrong size!");
struct Rule {
s32 timecnt;
s32 typecnt;
s32 charcnt;
bool goback;
bool goahead;
std::array <u8, 0x2> padding0;
std::array<s64, TZ_MAX_TIMES> ats;
std::array<u8, TZ_MAX_TIMES> types;
std::array<ttinfo, TZ_MAX_TYPES> ttis;
std::array<char, std::max(MAX_ZONE_CHARS, MAX_TZNAME_CHARS)> chars;
s32 defaulttype;
std::array <u8, 0x12C4> padding1;
};
static_assert(sizeof(Rule) == 0x4000, "Rule has the wrong size!");
struct CalendarTimeInternal {
s32 tm_sec;
s32 tm_min;
s32 tm_hour;
s32 tm_mday;
s32 tm_mon;
s32 tm_year;
s32 tm_wday;
s32 tm_yday;
s32 tm_isdst;
std::array<char, 16> tm_zone;
s32 tm_utoff;
s32 time_index;
};
static_assert(sizeof(CalendarTimeInternal) == 0x3C, "CalendarTimeInternal has the wrong size!");
s32 ParseTimeZoneBinary(Rule& out_rule, std::span<const u8> binary);
bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep);
u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp);
} // namespace Tz

View File

@@ -49,7 +49,6 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
import java.text.NumberFormat
@@ -171,11 +170,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
stopMotionSensorListener()
}
override fun onStop() {
super.onStop()
NativeConfig.saveGlobalConfig()
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {

View File

@@ -554,6 +554,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
}
popup.setOnDismissListener { NativeConfig.saveGlobalConfig() }
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_toggle_fps -> {
@@ -720,7 +721,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.emulation_control_adjust)
.setView(adjustBinding.root)
.setPositiveButton(android.R.string.ok, null)
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
NativeConfig.saveGlobalConfig()
}
.setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
setControlScale(50)
setControlOpacity(100)

View File

@@ -21,7 +21,7 @@ void AndroidConfig::ReloadAllValues() {
}
void AndroidConfig::SaveAllValues() {
Save();
SaveValues();
SaveAndroidValues();
}

View File

@@ -30,27 +30,27 @@ NativeClock::NativeClock() {
}
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)};
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
}
std::chrono::microseconds NativeClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)};
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)};
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
}
u64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor);
s64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
}
u64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor);
s64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
}
u64 NativeClock::GetHostTicksNow() const {
u64 cntvct_el0 = 0;
s64 NativeClock::GetUptime() const {
s64 cntvct_el0 = 0;
asm volatile("dsb ish\n\t"
"mrs %[cntvct_el0], cntvct_el0\n\t"
"dsb ish\n\t"
@@ -58,15 +58,11 @@ u64 NativeClock::GetHostTicksNow() const {
return cntvct_el0;
}
u64 NativeClock::GetHostTicksElapsed() const {
return GetHostTicksNow();
}
bool NativeClock::IsNative() const {
return true;
}
u64 NativeClock::GetHostCNTFRQ() {
s64 NativeClock::GetHostCNTFRQ() {
u64 cntfrq_el0 = 0;
std::string_view board{""};
#ifdef ANDROID

View File

@@ -17,17 +17,15 @@ public:
std::chrono::milliseconds GetTimeMS() const override;
u64 GetCNTPCT() const override;
s64 GetCNTPCT() const override;
u64 GetGPUTick() const override;
s64 GetGPUTick() const override;
u64 GetHostTicksNow() const override;
u64 GetHostTicksElapsed() const override;
s64 GetUptime() const override;
bool IsNative() const override;
static u64 GetHostCNTFRQ();
static s64 GetHostCNTFRQ();
public:
using FactorType = unsigned __int128;

View File

@@ -18,4 +18,4 @@ struct MemoryInfo {
*/
[[nodiscard]] const MemoryInfo& GetMemInfo();
} // namespace Common
} // namespace Common

View File

@@ -419,9 +419,16 @@ struct Values {
linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
SwitchableSetting<s64> custom_rtc{
linkage, 0, "custom_rtc", Category::System, Specialization::Time,
true, true, &custom_rtc_enabled};
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
s64 custom_rtc_differential;
false, true, &custom_rtc_enabled};
SwitchableSetting<s64, true> custom_rtc_offset{linkage,
0,
std::numeric_limits<int>::min(),
std::numeric_limits<int>::max(),
"custom_rtc_offset",
Category::System,
Specialization::Countable,
true,
true};
SwitchableSetting<bool> rng_seed_enabled{
linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
SwitchableSetting<u32> rng_seed{

View File

@@ -88,7 +88,17 @@ std::string FindSystemTimeZone() {
LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index);
}
}
return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours));
// For some reason the Etc/GMT times are reversed. GMT+6 contains -21600 as its offset,
// -6 hours instead of +6 hours, so these signs are purposefully reversed to fix it.
std::string postfix{""};
if (hours > 0) {
postfix = fmt::format("-{:d}", std::abs(hours));
} else if (hours < 0) {
postfix = fmt::format("+{:d}", std::abs(hours));
}
return fmt::format("Etc/GMT{:s}", postfix);
}
} // namespace Common::TimeZone

View File

@@ -12,9 +12,8 @@
namespace Common {
struct UUID {
std::array<u8, 0x10> uuid{};
std::array<u8, 0x10> uuid;
/// Constructs an invalid UUID.
constexpr UUID() = default;
/// Constructs a UUID from a reference to a 128 bit array.
@@ -34,14 +33,6 @@ struct UUID {
*/
explicit UUID(std::string_view uuid_string);
~UUID() = default;
constexpr UUID(const UUID&) noexcept = default;
constexpr UUID(UUID&&) noexcept = default;
constexpr UUID& operator=(const UUID&) noexcept = default;
constexpr UUID& operator=(UUID&&) noexcept = default;
/**
* Returns whether the stored UUID is valid or not.
*
@@ -121,6 +112,7 @@ struct UUID {
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
};
static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
static_assert(std::is_trivial_v<UUID>);
/// An invalid UUID. This UUID has all its bytes set to 0.
constexpr UUID InvalidUUID = {};

View File

@@ -18,42 +18,40 @@ namespace Common {
class StandardWallClock final : public WallClock {
public:
explicit StandardWallClock() : start_time{SteadyClock::Now()} {}
explicit StandardWallClock() {}
std::chrono::nanoseconds GetTimeNS() const override {
return SteadyClock::Now() - start_time;
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
std::chrono::microseconds GetTimeUS() const override {
return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den);
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
std::chrono::milliseconds GetTimeMS() const override {
return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den);
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
u64 GetCNTPCT() const override {
return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
s64 GetCNTPCT() const override {
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
u64 GetGPUTick() const override {
return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
s64 GetGPUTick() const override {
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
u64 GetHostTicksNow() const override {
return static_cast<u64>(SteadyClock::Now().time_since_epoch().count());
}
u64 GetHostTicksElapsed() const override {
return static_cast<u64>(GetTimeNS().count());
s64 GetUptime() const override {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
bool IsNative() const override {
return false;
}
private:
SteadyClock::time_point start_time;
};
std::unique_ptr<WallClock> CreateOptimalClock() {

View File

@@ -29,16 +29,13 @@ public:
virtual std::chrono::milliseconds GetTimeMS() const = 0;
/// @returns The guest CNTPCT ticks since the construction of this clock.
virtual u64 GetCNTPCT() const = 0;
virtual s64 GetCNTPCT() const = 0;
/// @returns The guest GPU ticks since the construction of this clock.
virtual u64 GetGPUTick() const = 0;
virtual s64 GetGPUTick() const = 0;
/// @returns The raw host timer ticks since an indeterminate epoch.
virtual u64 GetHostTicksNow() const = 0;
/// @returns The raw host timer ticks since the construction of this clock.
virtual u64 GetHostTicksElapsed() const = 0;
virtual s64 GetUptime() const = 0;
/// @returns Whether the clock directly uses the host's hardware clock.
virtual bool IsNative() const = 0;

View File

@@ -8,39 +8,35 @@
namespace Common::X64 {
NativeClock::NativeClock(u64 rdtsc_frequency_)
: start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_},
ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)},
: rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den,
rdtsc_frequency)},
us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)};
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
}
std::chrono::microseconds NativeClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)};
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)};
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
}
u64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor);
s64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
}
u64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor);
s64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
}
u64 NativeClock::GetHostTicksNow() const {
return FencedRDTSC();
}
u64 NativeClock::GetHostTicksElapsed() const {
return FencedRDTSC() - start_ticks;
s64 NativeClock::GetUptime() const {
return static_cast<s64>(FencedRDTSC());
}
bool NativeClock::IsNative() const {

View File

@@ -17,18 +17,15 @@ public:
std::chrono::milliseconds GetTimeMS() const override;
u64 GetCNTPCT() const override;
s64 GetCNTPCT() const override;
u64 GetGPUTick() const override;
s64 GetGPUTick() const override;
u64 GetHostTicksNow() const override;
u64 GetHostTicksElapsed() const override;
s64 GetUptime() const override;
bool IsNative() const override;
private:
u64 start_ticks;
u64 rdtsc_frequency;
u64 ns_rdtsc_factor;

View File

@@ -513,6 +513,24 @@ add_library(core STATIC
hle/service/glue/glue_manager.h
hle/service/glue/notif.cpp
hle/service/glue/notif.h
hle/service/glue/time/alarm_worker.cpp
hle/service/glue/time/alarm_worker.h
hle/service/glue/time/file_timestamp_worker.cpp
hle/service/glue/time/file_timestamp_worker.h
hle/service/glue/time/manager.cpp
hle/service/glue/time/manager.h
hle/service/glue/time/pm_state_change_handler.cpp
hle/service/glue/time/pm_state_change_handler.h
hle/service/glue/time/standard_steady_clock_resource.cpp
hle/service/glue/time/standard_steady_clock_resource.h
hle/service/glue/time/static.cpp
hle/service/glue/time/static.h
hle/service/glue/time/time_zone.cpp
hle/service/glue/time/time_zone.h
hle/service/glue/time/time_zone_binary.cpp
hle/service/glue/time/time_zone_binary.h
hle/service/glue/time/worker.cpp
hle/service/glue/time/worker.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
@@ -689,6 +707,46 @@ add_library(core STATIC
hle/service/prepo/prepo.h
hle/service/psc/psc.cpp
hle/service/psc/psc.h
hle/service/psc/time/alarms.cpp
hle/service/psc/time/alarms.h
hle/service/psc/time/clocks/context_writers.cpp
hle/service/psc/time/clocks/context_writers.h
hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
hle/service/psc/time/clocks/standard_local_system_clock_core.h
hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
hle/service/psc/time/clocks/standard_network_system_clock_core.h
hle/service/psc/time/clocks/standard_steady_clock_core.cpp
hle/service/psc/time/clocks/standard_steady_clock_core.h
hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
hle/service/psc/time/clocks/standard_user_system_clock_core.h
hle/service/psc/time/clocks/steady_clock_core.h
hle/service/psc/time/clocks/system_clock_core.cpp
hle/service/psc/time/clocks/system_clock_core.h
hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
hle/service/psc/time/clocks/tick_based_steady_clock_core.h
hle/service/psc/time/common.cpp
hle/service/psc/time/common.h
hle/service/psc/time/errors.h
hle/service/psc/time/shared_memory.cpp
hle/service/psc/time/shared_memory.h
hle/service/psc/time/static.cpp
hle/service/psc/time/static.h
hle/service/psc/time/manager.h
hle/service/psc/time/power_state_service.cpp
hle/service/psc/time/power_state_service.h
hle/service/psc/time/service_manager.cpp
hle/service/psc/time/service_manager.h
hle/service/psc/time/steady_clock.cpp
hle/service/psc/time/steady_clock.h
hle/service/psc/time/system_clock.cpp
hle/service/psc/time/system_clock.h
hle/service/psc/time/time_zone.cpp
hle/service/psc/time/time_zone.h
hle/service/psc/time/time_zone_service.cpp
hle/service/psc/time/time_zone_service.h
hle/service/psc/time/power_state_request_manager.cpp
hle/service/psc/time/power_state_request_manager.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
hle/service/ptm/ptm.cpp
@@ -712,22 +770,23 @@ add_library(core STATIC
hle/service/server_manager.h
hle/service/service.cpp
hle/service/service.h
hle/service/set/appln_settings.cpp
hle/service/set/appln_settings.h
hle/service/set/device_settings.cpp
hle/service/set/device_settings.h
hle/service/set/setting_formats/appln_settings.cpp
hle/service/set/setting_formats/appln_settings.h
hle/service/set/setting_formats/device_settings.cpp
hle/service/set/setting_formats/device_settings.h
hle/service/set/setting_formats/system_settings.cpp
hle/service/set/setting_formats/system_settings.h
hle/service/set/setting_formats/private_settings.cpp
hle/service/set/setting_formats/private_settings.h
hle/service/set/factory_settings_server.cpp
hle/service/set/factory_settings_server.h
hle/service/set/firmware_debug_settings_server.cpp
hle/service/set/firmware_debug_settings_server.h
hle/service/set/private_settings.cpp
hle/service/set/private_settings.h
hle/service/set/settings.cpp
hle/service/set/settings.h
hle/service/set/settings_server.cpp
hle/service/set/settings_server.h
hle/service/set/system_settings.cpp
hle/service/set/system_settings.h
hle/service/set/settings_types.h
hle/service/set/system_settings_server.cpp
hle/service/set/system_settings_server.h
hle/service/sm/sm.cpp
@@ -755,40 +814,6 @@ add_library(core STATIC
hle/service/ssl/ssl.cpp
hle/service/ssl/ssl.h
hle/service/ssl/ssl_backend.h
hle/service/time/clock_types.h
hle/service/time/ephemeral_network_system_clock_context_writer.h
hle/service/time/ephemeral_network_system_clock_core.h
hle/service/time/errors.h
hle/service/time/local_system_clock_context_writer.h
hle/service/time/network_system_clock_context_writer.h
hle/service/time/standard_local_system_clock_core.h
hle/service/time/standard_network_system_clock_core.h
hle/service/time/standard_steady_clock_core.cpp
hle/service/time/standard_steady_clock_core.h
hle/service/time/standard_user_system_clock_core.cpp
hle/service/time/standard_user_system_clock_core.h
hle/service/time/steady_clock_core.h
hle/service/time/system_clock_context_update_callback.cpp
hle/service/time/system_clock_context_update_callback.h
hle/service/time/system_clock_core.cpp
hle/service/time/system_clock_core.h
hle/service/time/tick_based_steady_clock_core.cpp
hle/service/time/tick_based_steady_clock_core.h
hle/service/time/time.cpp
hle/service/time/time.h
hle/service/time/time_interface.cpp
hle/service/time/time_interface.h
hle/service/time/time_manager.cpp
hle/service/time/time_manager.h
hle/service/time/time_sharedmemory.cpp
hle/service/time/time_sharedmemory.h
hle/service/time/time_zone_content_manager.cpp
hle/service/time/time_zone_content_manager.h
hle/service/time/time_zone_manager.cpp
hle/service/time/time_zone_manager.h
hle/service/time/time_zone_service.cpp
hle/service/time/time_zone_service.h
hle/service/time/time_zone_types.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/display/vi_display.cpp
@@ -869,7 +894,7 @@ endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb)
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})

View File

@@ -39,9 +39,14 @@
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/psc/time/steady_clock.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -129,8 +134,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {}
: kernel{system}, fs_controller{system}, hid_core{}, room_network{},
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {}
void Initialize(System& system) {
device_memory = std::make_unique<Core::DeviceMemory>();
@@ -142,8 +147,6 @@ struct System::Impl {
core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
RefreshTime();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr) {
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -181,14 +184,57 @@ struct System::Impl {
Initialize(system);
}
void RefreshTime() {
void RefreshTime(System& system) {
if (!system.IsPoweredOn()) {
return;
}
auto settings_service =
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys",
true);
auto static_service_a =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true);
auto static_service_s =
system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true);
std::shared_ptr<Service::PSC::Time::SystemClock> user_clock;
static_service_a->GetStandardUserSystemClock(user_clock);
std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
static_service_a->GetStandardLocalSystemClock(local_clock);
std::shared_ptr<Service::PSC::Time::SystemClock> network_clock;
static_service_s->GetStandardNetworkSystemClock(network_clock);
std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service;
static_service_a->GetTimeZoneService(timezone_service);
Service::PSC::Time::LocationName name{};
auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
std::memcpy(name.name.data(), new_name.data(), std::min(name.name.size(), new_name.size()));
timezone_service->SetDeviceLocation(name);
u64 time_offset = 0;
if (Settings::values.custom_rtc_enabled) {
time_offset = Settings::values.custom_rtc_offset.GetValue();
}
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
const auto current_time =
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
Settings::values.custom_rtc_differential =
(Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue()
: current_time) -
current_time;
const u64 current_time =
+std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
const u64 new_time = current_time + time_offset;
Service::PSC::Time::SystemClockContext context{};
settings_service->SetUserSystemClockContext(context);
user_clock->SetCurrentTime(new_time);
local_clock->SetCurrentTime(new_time);
network_clock->GetSystemClockContext(context);
settings_service->SetNetworkSystemClockContext(context);
network_clock->SetCurrentTime(new_time);
}
void Run() {
@@ -264,9 +310,6 @@ struct System::Impl {
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
services = std::make_unique<Service::Services>(service_manager, system);
// Initialize time manager, which must happen after kernel is created
time_manager.Initialize();
is_powered_on = true;
exit_locked = false;
exit_requested = false;
@@ -416,7 +459,6 @@ struct System::Impl {
fs_controller.Reset();
cheat_engine.reset();
telemetry_session.reset();
time_manager.Shutdown();
core_timing.ClearPendingEvents();
app_loader.reset();
audio_core.reset();
@@ -532,7 +574,6 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::Account::ProfileManager profile_manager;
Service::Time::TimeManager time_manager;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -901,14 +942,6 @@ const Service::Account::ProfileManager& System::GetProfileManager() const {
return impl->profile_manager;
}
Service::Time::TimeManager& System::GetTimeManager() {
return impl->time_manager;
}
const Service::Time::TimeManager& System::GetTimeManager() const {
return impl->time_manager;
}
void System::SetExitLocked(bool locked) {
impl->exit_locked = locked;
}
@@ -1020,13 +1053,9 @@ void System::Exit() {
}
void System::ApplySettings() {
impl->RefreshTime();
impl->RefreshTime(*this);
if (IsPoweredOn()) {
if (Settings::values.custom_rtc_enabled) {
const s64 posix_time{Settings::values.custom_rtc.GetValue()};
GetTimeManager().UpdateLocalSystemClockTime(posix_time);
}
Renderer().RefreshBaseSettings();
}
}

View File

@@ -72,10 +72,6 @@ namespace SM {
class ServiceManager;
} // namespace SM
namespace Time {
class TimeManager;
} // namespace Time
} // namespace Service
namespace Tegra {
@@ -377,9 +373,6 @@ public:
[[nodiscard]] Service::Account::ProfileManager& GetProfileManager();
[[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const;
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
[[nodiscard]] Core::Debugger& GetDebugger();
[[nodiscard]] const Core::Debugger& GetDebugger() const;

View File

@@ -157,7 +157,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
}
}
for (auto h : to_remove) {
for (auto& h : to_remove) {
event_queue.erase(h);
}

View File

@@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
}};
VirtualFile SynthesizeSystemArchive(const u64 title_id) {
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id)
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) {
return nullptr;
}
const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
if (desc.supplier == nullptr)
if (desc.supplier == nullptr) {
return nullptr;
}
const auto dir = desc.supplier();
if (dir == nullptr)
if (dir == nullptr) {
return nullptr;
}
const auto romfs = CreateRomFS(dir);
if (romfs == nullptr)
if (romfs == nullptr) {
return nullptr;
}
LOG_INFO(Service_FS, " - System archive generation successful!");
return romfs;

View File

@@ -6,7 +6,6 @@
#include "common/swap.h"
#include "core/file_sys/system_archive/time_zone_binary.h"
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/time/time_zone_types.h"
#include "nx_tzdb.h"

View File

@@ -10,8 +10,10 @@
#include "core/core.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_result.h"
#include "core/hle/service/time/time_manager.h"
#include "core/hle/service/time/time_zone_content_manager.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Capture {
@@ -239,10 +241,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
const ApplicationData& app_data, std::span<const u8> image_data,
u64 aruid) {
const u64 title_id = system.GetApplicationProcessProgramID();
const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore();
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
static_service->GetStandardUserSystemClock(user_clock);
s64 posix_time{};
Result result = user_clock.GetCurrentTime(system, posix_time);
auto result = user_clock->GetCurrentTime(posix_time);
if (result.IsError()) {
return result;
@@ -257,10 +264,14 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry,
const ScreenShotAttribute& attribute,
const AlbumFileId& file_id,
std::span<const u8> image_data) {
const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore();
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
static_service->GetStandardUserSystemClock(user_clock);
s64 posix_time{};
Result result = user_clock.GetCurrentTime(system, posix_time);
auto result = user_clock->GetCurrentTime(posix_time);
if (result.IsError()) {
return result;
@@ -455,19 +466,23 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const
}
AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const {
Time::TimeZone::CalendarInfo calendar_date{};
const auto& time_zone_manager =
system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date);
std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
static_service->GetTimeZoneService(timezone_service);
Service::PSC::Time::CalendarTime calendar_time{};
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time);
return {
.year = calendar_date.time.year,
.month = calendar_date.time.month,
.day = calendar_date.time.day,
.hour = calendar_date.time.hour,
.minute = calendar_date.time.minute,
.second = calendar_date.time.second,
.year = calendar_time.year,
.month = calendar_time.month,
.day = calendar_time.day,
.hour = calendar_time.hour,
.minute = calendar_time.minute,
.second = calendar_time.second,
.unique_id = 0,
};
}

View File

@@ -8,6 +8,9 @@
#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/glue/notif.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/server_manager.h"
namespace Service::Glue {
@@ -31,6 +34,22 @@ void LoopProcess(Core::System& system) {
// Notification Services for application
server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system));
// Time
auto time = std::make_shared<Time::TimeManager>(system);
server_manager->RegisterNamedService(
"time:u",
std::make_shared<Time::StaticService>(
system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u"));
server_manager->RegisterNamedService(
"time:a",
std::make_shared<Time::StaticService>(
system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a"));
server_manager->RegisterNamedService(
"time:r",
std::make_shared<Time::StaticService>(
system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r"));
ServerManager::RunServer(std::move(server_manager));
}

View File

@@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/alarm_worker.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Glue::Time {
AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource)
: m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{
steady_clock_resource} {}
AlarmWorker::~AlarmWorker() {
m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
m_ctx.CloseEvent(m_timer_event);
}
void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
m_time_m = std::move(time_m);
m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
m_timer_timing_event = Core::Timing::CreateEvent(
"Glue:AlarmWorker::AlarmTimer",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
m_timer_event->Signal();
return std::nullopt;
});
AttachToClosestAlarmEvent();
}
bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info,
s64& out_time) {
bool is_valid{};
Service::PSC::Time::AlarmInfo alarm_info{};
s64 closest_time{};
auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time);
ASSERT(res == ResultSuccess);
if (is_valid) {
out_alarm_info = alarm_info;
out_time = closest_time;
}
return is_valid;
}
void AlarmWorker::OnPowerStateChanged() {
Service::PSC::Time::AlarmInfo closest_alarm_info{};
s64 closest_time{};
if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) {
m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
m_timer_event->Clear();
return;
}
if (closest_alarm_info.alert_time <= closest_time) {
m_time_m->CheckAndSignalAlarms();
} else {
auto next_time{closest_alarm_info.alert_time - closest_time};
m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
m_timer_event->Clear();
m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time),
m_timer_timing_event);
}
}
Result AlarmWorker::AttachToClosestAlarmEvent() {
m_time_m->GetClosestAlarmUpdatedEvent(&m_event);
R_SUCCEED();
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/common.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class ServiceManager;
}
namespace Service::Glue::Time {
class StandardSteadyClockResource;
class AlarmWorker {
public:
explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource);
~AlarmWorker();
void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m);
Kernel::KEvent& GetEvent() {
return *m_event;
}
Kernel::KEvent& GetTimerEvent() {
return *m_timer_event;
}
void OnPowerStateChanged();
private:
bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time);
Result AttachToClosestAlarmEvent();
Core::System& m_system;
KernelHelpers::ServiceContext m_ctx;
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
Kernel::KEvent* m_event{};
Kernel::KEvent* m_timer_event{};
std::shared_ptr<Core::Timing::EventType> m_timer_timing_event;
StandardSteadyClockResource& m_steady_clock_resource;
};
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"
namespace Service::Glue::Time {
void FileTimestampWorker::SetFilesystemPosixTime() {
s64 time{};
Service::PSC::Time::CalendarTime calendar_time{};
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
if (m_initialized && m_system_clock->GetCurrentTime(time) == ResultSuccess &&
m_time_zone->ToCalendarTimeWithMyRule(calendar_time, additional_info, time) ==
ResultSuccess) {
// TODO IFileSystemProxy::SetCurrentPosixTime
}
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_types.h"
namespace Service::PSC::Time {
class SystemClock;
class TimeZoneService;
} // namespace Service::PSC::Time
namespace Service::Glue::Time {
class FileTimestampWorker {
public:
FileTimestampWorker() = default;
void SetFilesystemPosixTime();
std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{};
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{};
bool m_initialized{};
};
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,277 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include "core/core.h"
#include "core/core_timing.h"
#include "common/settings.h"
#include "common/time_zone.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Glue::Time {
namespace {
template <typename T>
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
const char* category, const char* name) {
std::vector<u8> interval_buf;
auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
ASSERT(res == ResultSuccess);
T v{};
std::memcpy(&v, interval_buf.data(), sizeof(T));
return v;
}
s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
constexpr auto is_leap = [](s32 year) -> bool {
return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
};
constexpr std::array<s32, 12> MonthStartDayOfYear{
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
};
s16 month_s16{calendar.month};
s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) +
((month_s16 * 43) >> 9))};
s8 month_index{static_cast<s8>(calendar.month - 12 * month)};
if (month_index == 0) {
month_index = 12;
}
s32 year{(month + calendar.year) - !month_index};
s32 v8{year >= 0 ? year : year + 3};
s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1];
days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365;
if (month_index <= 2 && is_leap(year)) {
days_since_epoch--;
}
auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll +
calendar.second};
return epoch_s - 62135683200ll;
}
s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) {
Service::PSC::Time::CalendarTime calendar{
.year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"),
.month = 1,
.day = 1,
.hour = 0,
.minute = 0,
.second = 0,
};
return CalendarTimeToEpoch(calendar);
}
Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) {
auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
Service::PSC::Time::LocationName configured_name{};
std::memcpy(configured_name.name.data(), configured_zone.data(),
std::min(configured_name.name.size(), configured_zone.size()));
if (!IsTimeZoneBinaryValid(configured_name)) {
configured_zone = Common::TimeZone::FindSystemTimeZone();
configured_name = {};
std::memcpy(configured_name.name.data(), configured_zone.data(),
std::min(configured_name.name.size(), configured_zone.size()));
}
ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!",
configured_name.name.data());
return configured_name;
}
} // namespace
TimeManager::TimeManager(Core::System& system)
: m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource,
m_file_timestamp_worker} {
m_time_m =
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm);
ASSERT(res == ResultSuccess);
m_set_sys =
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
res = MountTimeZoneBinary(system);
ASSERT(res == ResultSuccess);
m_worker.Initialize(m_time_sm, m_set_sys);
res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock);
ASSERT(res == ResultSuccess);
res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone);
ASSERT(res == ResultSuccess);
res = SetupStandardSteadyClockCore();
ASSERT(res == ResultSuccess);
Service::PSC::Time::SystemClockContext user_clock_context{};
res = m_set_sys->GetUserSystemClockContext(user_clock_context);
ASSERT(res == ResultSuccess);
// TODO the local clock should initialise with this epoch time, and be updated somewhere else on
// first boot to update it, but I haven't been able to find that point (likely via ntc's auto
// correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for
// first boot, grab the current real seconds.
auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)};
if (user_clock_context == Service::PSC::Time::SystemClockContext{}) {
m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time);
}
res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time);
ASSERT(res == ResultSuccess);
Service::PSC::Time::SystemClockContext network_clock_context{};
res = m_set_sys->GetNetworkSystemClockContext(network_clock_context);
ASSERT(res == ResultSuccess);
auto network_accuracy_m{GetSettingsItemValue<s32>(
m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")};
auto one_minute_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
s64 network_accuracy_ns{network_accuracy_m * one_minute_ns};
res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns);
ASSERT(res == ResultSuccess);
bool is_automatic_correction_enabled{};
res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled);
ASSERT(res == ResultSuccess);
Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{};
res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime(
automatic_correction_time_point);
ASSERT(res == ResultSuccess);
res = m_time_m->SetupStandardUserSystemClockCore(automatic_correction_time_point,
is_automatic_correction_enabled);
ASSERT(res == ResultSuccess);
res = m_time_m->SetupEphemeralNetworkSystemClockCore();
ASSERT(res == ResultSuccess);
res = SetupTimeZoneServiceCore();
ASSERT(res == ResultSuccess);
s64 rtc_time_s{};
res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s);
ASSERT(res == ResultSuccess);
// TODO system report "launch"
// "rtc_reset" = m_steady_clock_resource.m_rtc_reset
// "rtc_value" = rtc_time_s
m_worker.StartThread();
m_file_timestamp_worker.m_initialized = true;
s64 system_clock_time{};
if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(system_clock_time) ==
ResultSuccess) {
Service::PSC::Time::CalendarTime calendar_time{};
Service::PSC::Time::CalendarAdditionalInfo calendar_additional{};
if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule(
calendar_time, calendar_additional, system_clock_time) == ResultSuccess) {
// TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time,
// calendar_additional.ut_offset)
}
}
}
Result TimeManager::SetupStandardSteadyClockCore() {
Common::UUID external_clock_source_id{};
auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id);
ASSERT(res == ResultSuccess);
s64 external_steady_clock_internal_offset_s{};
res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s);
ASSERT(res == ResultSuccess);
auto one_second_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s *
one_second_ns};
s32 standard_steady_clock_test_offset_m{
GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")};
auto one_minute_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns};
auto reset_detected = m_steady_clock_resource.GetResetDetected();
if (reset_detected) {
external_clock_source_id = {};
}
Common::UUID clock_source_id{};
m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id);
if (clock_source_id != external_clock_source_id) {
m_set_sys->SetExternalSteadyClockSourceId(clock_source_id);
}
res = m_time_m->SetupStandardSteadyClockCore(clock_source_id, m_steady_clock_resource.GetTime(),
external_steady_clock_internal_offset_ns,
standard_steady_clock_test_offset_ns,
reset_detected);
ASSERT(res == ResultSuccess);
R_SUCCEED();
}
Result TimeManager::SetupTimeZoneServiceCore() {
Service::PSC::Time::LocationName name{};
auto res = m_set_sys->GetDeviceTimeZoneLocationName(name);
ASSERT(res == ResultSuccess);
auto configured_zone = GetTimeZoneString(name);
if (configured_zone.name != name.name) {
m_set_sys->SetDeviceTimeZoneLocationName(configured_zone);
name = configured_zone;
std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
m_time_sm->GetStandardLocalSystemClock(local_clock);
Service::PSC::Time::SystemClockContext context{};
local_clock->GetSystemClockContext(context);
m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point);
}
Service::PSC::Time::SteadyClockTimePoint time_point{};
res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point);
ASSERT(res == ResultSuccess);
auto location_count = GetTimeZoneCount();
Service::PSC::Time::RuleVersion rule_version{};
GetTimeZoneVersion(rule_version);
std::span<const u8> rule_buffer{};
size_t rule_size{};
res = GetTimeZoneRule(rule_buffer, rule_size, name);
ASSERT(res == ResultSuccess);
res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count,
rule_buffer);
ASSERT(res == ResultSuccess);
R_SUCCEED();
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class ServiceManager;
class StaticService;
} // namespace Service::PSC::Time
namespace Service::Glue::Time {
class TimeManager {
public:
explicit TimeManager(Core::System& system);
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{};
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{};
StandardSteadyClockResource m_steady_clock_resource;
FileTimestampWorker m_file_timestamp_worker;
TimeWorker m_worker;
private:
Result SetupStandardSteadyClockCore();
Result SetupTimeZoneServiceCore();
};
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/glue/time/pm_state_change_handler.h"
namespace Service::Glue::Time {
PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker)
: m_alarm_worker{alarm_worker} {
// TODO Initialize IPmModule, dependent on Rtc and Fs
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Service::Glue::Time {
class AlarmWorker;
class PmStateChangeHandler {
public:
explicit PmStateChangeHandler(AlarmWorker& alarm_worker);
AlarmWorker& m_alarm_worker;
s32 m_priority{};
};
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/psc/time/errors.h"
namespace Service::Glue::Time {
namespace {
[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001;
[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001;
Result GetTimeInSeconds(Core::System& system, s64& out_time_s) {
out_time_s = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
if (Settings::values.custom_rtc_enabled) {
out_time_s += Settings::values.custom_rtc_offset.GetValue();
}
R_SUCCEED();
}
} // namespace
StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {}
void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id,
Common::UUID* external_source_id) {
constexpr size_t NUM_TRIES{20};
size_t i{0};
Result res{ResultSuccess};
for (; i < NUM_TRIES; i++) {
res = SetCurrentTime();
if (res == ResultSuccess) {
break;
}
Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::milliseconds(1))
.count());
}
if (i < NUM_TRIES) {
m_set_time_result = ResultSuccess;
if (*external_source_id != Service::PSC::Time::ClockSourceId{}) {
m_clock_source_id = *external_source_id;
} else {
m_clock_source_id = Common::UUID::MakeRandom();
}
} else {
m_set_time_result = res;
auto ticks{m_system.CoreTiming().GetClockTicks()};
m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count();
m_clock_source_id = Common::UUID::MakeRandom();
}
if (out_source_id) {
*out_source_id = m_clock_source_id;
}
}
bool StandardSteadyClockResource::GetResetDetected() {
// TODO:
// call Rtc::GetRtcResetDetected(Max77620RtcSession)
// if detected:
// SetSys::SetExternalSteadyClockSourceId(invalid_id)
// Rtc::ClearRtcResetDetected(Max77620RtcSession)
// set m_rtc_reset to result
// Instead, only set reset to true if we're booting for the first time.
m_rtc_reset = false;
return m_rtc_reset;
}
Result StandardSteadyClockResource::SetCurrentTime() {
auto start_tick{m_system.CoreTiming().GetClockTicks()};
s64 rtc_time_s{};
// TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession))
R_TRY(GetTimeInSeconds(m_system, rtc_time_s));
auto end_tick{m_system.CoreTiming().GetClockTicks()};
auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)};
// Why is this here?
R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout);
auto one_second_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
s64 boot_time{rtc_time_s * one_second_ns -
Service::PSC::Time::ConvertToTimeSpan(end_tick).count()};
std::scoped_lock l{m_mutex};
m_time = boot_time;
R_SUCCEED();
}
Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) {
// TODO
// R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession)
R_RETURN(GetTimeInSeconds(m_system, out_time));
}
void StandardSteadyClockResource::UpdateTime() {
constexpr size_t NUM_TRIES{3};
size_t i{0};
Result res{ResultSuccess};
for (; i < NUM_TRIES; i++) {
res = SetCurrentTime();
if (res == ResultSuccess) {
break;
}
Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::milliseconds(1))
.count());
}
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/psc/time/common.h"
namespace Core {
class System;
}
namespace Service::Glue::Time {
class StandardSteadyClockResource {
public:
StandardSteadyClockResource(Core::System& system);
void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id);
s64 GetTime() const {
return m_time;
}
bool GetResetDetected();
Result SetCurrentTime();
Result GetRtcTimeInSeconds(s64& out_time);
void UpdateTime();
private:
Core::System& m_system;
std::mutex m_mutex;
Service::PSC::Time::ClockSourceId m_clock_source_id{};
s64 m_time{};
Result m_set_time_result;
bool m_rtc_reset;
};
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,448 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/errors.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/psc/time/steady_clock.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Glue::Time {
namespace {
template <typename T>
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
const char* category, const char* name) {
std::vector<u8> interval_buf;
auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
ASSERT(res == ResultSuccess);
T v{};
std::memcpy(&v, interval_buf.data(), sizeof(T));
return v;
}
} // namespace
StaticService::StaticService(Core::System& system_,
Service::PSC::Time::StaticServiceSetupInfo setup_info,
std::shared_ptr<TimeManager> time, const char* name)
: ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m},
m_setup_info{setup_info}, m_time_sm{time->m_time_sm},
m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{
time->m_steady_clock_resource} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
{1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
{2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
{3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
{4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
{5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
{20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
{50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
{51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
{100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
{101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
{102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
{200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
{201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
{300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
{400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
{401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
{500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
{501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
};
// clang-format on
RegisterHandlers(functions);
m_set_sys =
m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock &&
!m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location &&
!m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
m_time_m->GetStaticServiceAsAdmin(m_wrapped_service);
} else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
!m_setup_info.can_write_network_clock &&
!m_setup_info.can_write_timezone_device_location &&
!m_setup_info.can_write_steady_clock &&
!m_setup_info.can_write_uninitialized_clock) {
m_time_m->GetStaticServiceAsUser(m_wrapped_service);
} else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
!m_setup_info.can_write_network_clock &&
!m_setup_info.can_write_timezone_device_location &&
m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
m_time_m->GetStaticServiceAsRepair(m_wrapped_service);
} else {
UNREACHABLE();
}
auto res = m_wrapped_service->GetTimeZoneService(m_time_zone);
ASSERT(res == ResultSuccess);
}
void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
auto res = GetStandardUserSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
}
void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
auto res = GetStandardNetworkSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
}
void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<Service::PSC::Time::SteadyClock> service{};
auto res = GetStandardSteadyClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(service));
}
void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<TimeZoneService> service{};
auto res = GetTimeZoneService(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(service));
}
void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
auto res = GetStandardLocalSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
}
void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<Service::PSC::Time::SystemClock> service{};
auto res = GetEphemeralNetworkSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
}
void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KSharedMemory* shared_memory{};
auto res = GetSharedMemoryNativeHandle(&shared_memory);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(shared_memory);
}
void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto offset_ns{rp.Pop<s64>()};
auto res = SetStandardSteadyClockInternalOffset(offset_ns);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
s64 rtc_value{};
auto res = GetStandardSteadyClockRtcValue(rtc_value);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(rtc_value);
}
void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
bool is_enabled{};
auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push<bool>(is_enabled);
}
void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto automatic_correction{rp.Pop<bool>()};
auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
s32 initial_year{};
auto res = GetStandardUserSystemClockInitialYear(initial_year);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(initial_year);
}
void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
bool is_sufficient{};
auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push<bool>(is_sufficient);
}
void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Service::PSC::Time::SteadyClockTimePoint time_point{};
auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
IPC::ResponseBuilder rb{ctx,
2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
}
void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
s64 time{};
auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push<s64>(time);
}
void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto type{rp.PopEnum<Service::PSC::Time::TimeType>()};
Service::PSC::Time::ClockSnapshot snapshot{};
auto res = GetClockSnapshot(snapshot, type);
ctx.WriteBuffer(snapshot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()};
[[maybe_unused]] auto alignment{rp.Pop<u32>()};
auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
Service::PSC::Time::ClockSnapshot snapshot{};
auto res =
GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
ctx.WriteBuffer(snapshot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Service::PSC::Time::ClockSnapshot a{};
Service::PSC::Time::ClockSnapshot b{};
auto a_buffer{ctx.ReadBuffer(0)};
auto b_buffer{ctx.ReadBuffer(1)};
std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
s64 difference{};
auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(difference);
}
void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Service::PSC::Time::ClockSnapshot a{};
Service::PSC::Time::ClockSnapshot b{};
auto a_buffer{ctx.ReadBuffer(0)};
auto b_buffer{ctx.ReadBuffer(1)};
std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
s64 time{};
auto res = CalculateSpanBetween(time, a, b);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(time);
}
// =============================== Implementations ===========================
Result StaticService::GetStandardUserSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service));
}
Result StaticService::GetStandardNetworkSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service));
}
Result StaticService::GetStandardSteadyClock(
std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) {
R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service));
}
Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker,
m_setup_info.can_write_timezone_device_location,
m_time_zone);
R_SUCCEED();
}
Result StaticService::GetStandardLocalSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service));
}
Result StaticService::GetEphemeralNetworkSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service));
}
Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory));
}
Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied);
R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset(
offset_ns /
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()));
}
Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) {
R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value));
}
Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
bool& out_automatic_correction) {
R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
out_automatic_correction));
}
Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
bool automatic_correction) {
R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled(
automatic_correction));
}
Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) {
out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year");
R_SUCCEED();
}
Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
}
Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
out_time_point));
}
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
s64& out_time, Service::PSC::Time::SystemClockContext& context) {
R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
}
Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
Service::PSC::Time::TimeType type) {
R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
}
Result StaticService::GetClockSnapshotFromSystemClockContext(
Service::PSC::Time::ClockSnapshot& out_snapshot,
Service::PSC::Time::SystemClockContext& user_context,
Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) {
R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context,
network_context, type));
}
Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(
s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) {
R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
}
Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
Service::PSC::Time::ClockSnapshot& b) {
R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,110 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/time_zone.h"
#include "core/hle/service/psc/time/common.h"
namespace Core {
class System;
}
namespace Service::Set {
class ISystemSettingsServer;
}
namespace Service::PSC::Time {
class StaticService;
class SystemClock;
class SteadyClock;
class TimeZoneService;
class ServiceManager;
} // namespace Service::PSC::Time
namespace Service::Glue::Time {
class FileTimestampWorker;
class StandardSteadyClockResource;
class StaticService final : public ServiceFramework<StaticService> {
public:
explicit StaticService(Core::System& system,
Service::PSC::Time::StaticServiceSetupInfo setup_info,
std::shared_ptr<TimeManager> time, const char* name);
~StaticService() override = default;
Result GetStandardUserSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
Result GetStandardNetworkSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service);
Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
Result GetStandardLocalSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
Result GetEphemeralNetworkSystemClock(
std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
Result SetStandardSteadyClockInternalOffset(s64 offset);
Result GetStandardSteadyClockRtcValue(s64& out_rtc_value);
Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction);
Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
Result GetStandardUserSystemClockInitialYear(s32& out_year);
Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
Service::PSC::Time::SteadyClockTimePoint& out_time_point);
Result CalculateMonotonicSystemClockBaseTimePoint(
s64& out_time, Service::PSC::Time::SystemClockContext& context);
Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
Service::PSC::Time::TimeType type);
Result GetClockSnapshotFromSystemClockContext(
Service::PSC::Time::ClockSnapshot& out_snapshot,
Service::PSC::Time::SystemClockContext& user_context,
Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type);
Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
Service::PSC::Time::ClockSnapshot& a,
Service::PSC::Time::ClockSnapshot& b);
Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
Service::PSC::Time::ClockSnapshot& b);
private:
Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot,
Service::PSC::Time::SystemClockContext& user_context,
Service::PSC::Time::SystemClockContext& network_context,
Service::PSC::Time::TimeType type);
void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
void Handle_GetTimeZoneService(HLERequestContext& ctx);
void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
void Handle_GetClockSnapshot(HLERequestContext& ctx);
void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
void Handle_CalculateSpanBetween(HLERequestContext& ctx);
Core::System& m_system;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service;
Service::PSC::Time::StaticServiceSetupInfo m_setup_info;
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone;
FileTimestampWorker& m_file_timestamp_worker;
StandardSteadyClockResource& m_standard_steady_clock_resource;
};
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,377 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include "core/core.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/time_zone.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Glue::Time {
namespace {
static std::mutex g_list_mutex;
static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{};
} // namespace
TimeZoneService::TimeZoneService(
Core::System& system_, FileTimestampWorker& file_timestamp_worker,
bool can_write_timezone_device_location,
std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
: ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
m_can_write_timezone_device_location{can_write_timezone_device_location},
m_file_timestamp_worker{file_timestamp_worker},
m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
{1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
{2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
{3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
{4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
{5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
{6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
{7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
{8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
{20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
{100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
{101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
{201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
{202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
};
// clang-format on
RegisterHandlers(functions);
g_list_nodes.clear();
m_set_sys =
m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
}
TimeZoneService::~TimeZoneService() = default;
void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Service::PSC::Time::LocationName name{};
auto res = GetDeviceLocationName(name);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<Service::PSC::Time::LocationName>(name);
}
void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
auto res = SetDeviceLocation(name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
u32 count{};
auto res = GetTotalLocationNameCount(count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(count);
}
void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto index{rp.Pop<u32>()};
auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)};
std::vector<Service::PSC::Time::LocationName> names{};
u32 count{};
auto res = LoadLocationNameList(count, names, max_names, index);
ctx.WriteBuffer(names);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(count);
}
void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
Tz::Rule rule{};
auto res = LoadTimeZoneRule(rule, name);
ctx.WriteBuffer(rule);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Service::PSC::Time::RuleVersion rule_version{};
auto res = GetTimeZoneRuleVersion(rule_version);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<Service::PSC::Time::RuleVersion>(rule_version);
}
void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Service::PSC::Time::LocationName name{};
Service::PSC::Time::SteadyClockTimePoint time_point{};
auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
IPC::ResponseBuilder rb{ctx,
2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) +
(sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))};
rb.Push(res);
rb.PushRaw<Service::PSC::Time::LocationName>(name);
rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
}
void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
auto res = SetDeviceLocationNameWithTimeZoneRule();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Service::PSC::Time::ResultNotImplemented);
}
void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KEvent* event{};
auto res = GetDeviceLocationNameOperationEventReadableHandle(&event);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(event->GetReadableEvent());
}
void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto time{rp.Pop<s64>()};
auto rule_buffer{ctx.ReadBuffer()};
Tz::Rule rule{};
std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
Service::PSC::Time::CalendarTime calendar_time{};
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
IPC::ResponseBuilder rb{ctx,
2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
(sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
rb.Push(res);
rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
}
void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto time{rp.Pop<s64>()};
LOG_DEBUG(Service_Time, "called. time={}", time);
Service::PSC::Time::CalendarTime calendar_time{};
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
IPC::ResponseBuilder rb{ctx,
2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
(sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
rb.Push(res);
rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
}
void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}",
calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute,
calendar.second);
auto binary{ctx.ReadBuffer()};
Tz::Rule rule{};
std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
u32 count{};
std::array<s64, 2> times{};
u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
auto res = ToPosixTime(count, times, times_count, calendar, rule);
ctx.WriteBuffer(times);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(count);
}
void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
u32 count{};
std::array<s64, 2> times{};
u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
ctx.WriteBuffer(times);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(count);
}
// =============================== Implementations ===========================
Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) {
R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
}
Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& location_name) {
R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound);
std::scoped_lock l{m_mutex};
std::span<const u8> binary{};
size_t binary_size{};
R_TRY(GetTimeZoneRule(binary, binary_size, location_name))
R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary));
m_file_timestamp_worker.SetFilesystemPosixTime();
Service::PSC::Time::SteadyClockTimePoint time_point{};
Service::PSC::Time::LocationName name{};
R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(time_point, name));
m_set_sys->SetDeviceTimeZoneLocationName(name);
m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point);
std::scoped_lock m{g_list_mutex};
for (auto& operation_event : g_list_nodes) {
operation_event.m_event->Signal();
}
R_SUCCEED();
}
Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
}
Result TimeZoneService::LoadLocationNameList(
u32& out_count, std::vector<Service::PSC::Time::LocationName>& out_names, size_t max_names,
u32 index) {
std::scoped_lock l{m_mutex};
R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index));
}
Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule,
Service::PSC::Time::LocationName& name) {
std::scoped_lock l{m_mutex};
std::span<const u8> binary{};
size_t binary_size{};
R_TRY(GetTimeZoneRule(binary, binary_size, name))
R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary));
}
Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
}
Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
Service::PSC::Time::SteadyClockTimePoint& out_time_point,
Service::PSC::Time::LocationName& location_name) {
R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name));
}
Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() {
R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
R_RETURN(Service::PSC::Time::ResultNotImplemented);
}
Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
Kernel::KEvent** out_event) {
if (!operation_event_initialized) {
operation_event_initialized = false;
m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event);
m_operation_event.m_event =
m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent");
operation_event_initialized = true;
std::scoped_lock l{m_mutex};
g_list_nodes.push_back(m_operation_event);
}
*out_event = m_operation_event.m_event;
R_SUCCEED();
}
Result TimeZoneService::ToCalendarTime(
Service::PSC::Time::CalendarTime& out_calendar_time,
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) {
R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
}
Result TimeZoneService::ToCalendarTimeWithMyRule(
Service::PSC::Time::CalendarTime& out_calendar_time,
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) {
R_RETURN(
m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
}
Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
u32 out_times_count,
Service::PSC::Time::CalendarTime& calendar_time,
Tz::Rule& rule) {
R_RETURN(
m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
}
Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
u32 out_times_count,
Service::PSC::Time::CalendarTime& calendar_time) {
R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count,
calendar_time));
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <mutex>
#include <span>
#include <vector>
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Tz {
struct Rule;
}
namespace Service::Set {
class ISystemSettingsServer;
}
namespace Service::PSC::Time {
class TimeZoneService;
}
namespace Service::Glue::Time {
class FileTimestampWorker;
class TimeZoneService final : public ServiceFramework<TimeZoneService> {
public:
explicit TimeZoneService(
Core::System& system, FileTimestampWorker& file_timestamp_worker,
bool can_write_timezone_device_location,
std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service);
~TimeZoneService() override;
Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name);
Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name);
Result GetTotalLocationNameCount(u32& out_count);
Result LoadLocationNameList(u32& out_count,
std::vector<Service::PSC::Time::LocationName>& out_names,
size_t max_names, u32 index);
Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name);
Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version);
Result GetDeviceLocationNameAndUpdatedTime(
Service::PSC::Time::SteadyClockTimePoint& out_time_point,
Service::PSC::Time::LocationName& location_name);
Result SetDeviceLocationNameWithTimeZoneRule();
Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event);
Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time,
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time,
Tz::Rule& rule);
Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time,
Service::PSC::Time::CalendarAdditionalInfo& out_additional_info,
s64 time);
Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule);
Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
Service::PSC::Time::CalendarTime& calendar_time);
private:
void Handle_GetDeviceLocationName(HLERequestContext& ctx);
void Handle_SetDeviceLocationName(HLERequestContext& ctx);
void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
void Handle_LoadLocationNameList(HLERequestContext& ctx);
void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
void Handle_ToCalendarTime(HLERequestContext& ctx);
void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
void Handle_ToPosixTime(HLERequestContext& ctx);
void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
Core::System& m_system;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
bool m_can_write_timezone_device_location;
FileTimestampWorker& m_file_timestamp_worker;
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
std::mutex m_mutex;
bool operation_event_initialized{};
Service::PSC::Time::OperationEvent m_operation_event;
};
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,221 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
namespace Service::Glue::Time {
namespace {
constexpr u64 TimeZoneBinaryId = 0x10000000000080E;
static FileSys::VirtualDir g_time_zone_binary_romfs{};
static Result g_time_zone_binary_mount_result{ResultUnknown};
static std::vector<u8> g_time_zone_scratch_space(0x2800, 0);
Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size,
std::string_view path) {
R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result);
auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
R_UNLESS(vfs_file, ResultUnknown);
auto file_size{vfs_file->GetSize()};
R_UNLESS(file_size > 0, ResultUnknown);
R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed);
out_read_size = vfs_file->Read(out_buffer.data(), file_size);
R_UNLESS(out_read_size > 0, ResultUnknown);
R_SUCCEED();
}
} // namespace
void ResetTimeZoneBinary() {
g_time_zone_binary_romfs = {};
g_time_zone_binary_mount_result = ResultUnknown;
g_time_zone_scratch_space.clear();
g_time_zone_scratch_space.resize(0x2800, 0);
}
Result MountTimeZoneBinary(Core::System& system) {
ResetTimeZoneBinary();
auto& fsc{system.GetFileSystemController()};
std::unique_ptr<FileSys::NCA> nca{};
auto* bis_system = fsc.GetSystemNANDContents();
R_UNLESS(bis_system, ResultUnknown);
nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data);
if (nca) {
g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
}
if (g_time_zone_binary_romfs) {
// Validate that the romfs is readable, using invalid firmware keys can cause this to get
// set but the files to be garbage. In that case, we want to hit the next path and
// synthesise them instead.
Service::PSC::Time::LocationName name{"Etc/GMT"};
if (!IsTimeZoneBinaryValid(name)) {
ResetTimeZoneBinary();
}
}
if (!g_time_zone_binary_romfs) {
g_time_zone_binary_romfs = FileSys::ExtractRomFS(
FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
}
R_UNLESS(g_time_zone_binary_romfs, ResultUnknown);
g_time_zone_binary_mount_result = ResultSuccess;
R_SUCCEED();
}
void GetTimeZoneBinaryListPath(std::string& out_path) {
if (g_time_zone_binary_mount_result != ResultSuccess) {
return;
}
// out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary");
out_path = "/binaryList.txt";
}
void GetTimeZoneBinaryVersionPath(std::string& out_path) {
if (g_time_zone_binary_mount_result != ResultSuccess) {
return;
}
// out_path = fmt::format("{}:/version.txt", "TimeZoneBinary");
out_path = "/version.txt";
}
void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) {
if (g_time_zone_binary_mount_result != ResultSuccess) {
return;
}
// out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name);
out_path = fmt::format("/zoneinfo/{}", name.name.data());
}
bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
std::string path{};
GetTimeZoneZonePath(path, name);
auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
if (!vfs_file) {
LOG_INFO(Service_Time, "Could not find timezone file {}", path);
return false;
}
return vfs_file->GetSize() != 0;
}
u32 GetTimeZoneCount() {
std::string path{};
GetTimeZoneBinaryListPath(path);
size_t bytes_read{};
if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) {
return 0;
}
if (bytes_read == 0) {
return 0;
}
auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read);
u32 count{};
for (auto chr : chars) {
if (chr == '\n') {
count++;
}
}
return count;
}
Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
std::string path{};
GetTimeZoneBinaryVersionPath(path);
auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version),
sizeof(Service::PSC::Time::RuleVersion))};
size_t bytes_read{};
R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(),
path));
rule_version_buffer[bytes_read] = 0;
R_SUCCEED();
}
Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
Service::PSC::Time::LocationName& name) {
std::string path{};
GetTimeZoneZonePath(path, name);
size_t bytes_read{};
R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
g_time_zone_scratch_space.size(), path));
out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read);
out_rule_size = bytes_read;
R_SUCCEED();
}
Result GetTimeZoneLocationList(u32& out_count,
std::vector<Service::PSC::Time::LocationName>& out_names,
size_t max_names, u32 index) {
std::string path{};
GetTimeZoneBinaryListPath(path);
size_t bytes_read{};
R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
g_time_zone_scratch_space.size(), path));
out_count = 0;
R_SUCCEED_IF(bytes_read == 0);
Service::PSC::Time::LocationName current_name{};
size_t current_name_len{};
std::span<const u8> chars{g_time_zone_scratch_space};
u32 name_count{};
for (auto chr : chars) {
if (chr == '\r') {
continue;
}
if (chr == '\n') {
if (name_count >= index) {
out_names.push_back(current_name);
out_count++;
if (out_count >= max_names) {
break;
}
}
name_count++;
current_name_len = 0;
current_name = {};
continue;
}
if (chr == '\0') {
break;
}
R_UNLESS(current_name_len <= current_name.name.size() - 2,
Service::PSC::Time::ResultFailed);
current_name.name[current_name_len++] = chr;
}
R_SUCCEED();
}
} // namespace Service::Glue::Time

View File

@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <string>
#include <string_view>
#include "core/hle/service/psc/time/common.h"
namespace Core {
class System;
}
namespace Service::Glue::Time {
void ResetTimeZoneBinary();
Result MountTimeZoneBinary(Core::System& system);
void GetTimeZoneBinaryListPath(std::string& out_path);
void GetTimeZoneBinaryVersionPath(std::string& out_path);
void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name);
bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name);
u32 GetTimeZoneCount();
Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version);
Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
Service::PSC::Time::LocationName& name);
Result GetTimeZoneLocationList(u32& out_count,
std::vector<Service::PSC::Time::LocationName>& out_names,
size_t max_names, u32 index);
} // namespace Service::Glue::Time

View File

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

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/glue/time/alarm_worker.h"
#include "core/hle/service/glue/time/pm_state_change_handler.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::Set {
class ISystemSettingsServer;
}
namespace Service::PSC::Time {
class StaticService;
class SystemClock;
} // namespace Service::PSC::Time
namespace Service::Glue::Time {
class FileTimestampWorker;
class StandardSteadyClockResource;
class TimeWorker {
public:
explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
FileTimestampWorker& file_timestamp_worker);
~TimeWorker();
void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys);
void StartThread();
private:
void ThreadFunc(std::stop_token stop_token);
Core::System& m_system;
KernelHelpers::ServiceContext m_ctx;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
std::jthread m_thread;
Kernel::KEvent* m_event{};
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock;
std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock;
std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock;
StandardSteadyClockResource& m_steady_clock_resource;
FileTimestampWorker& m_file_timestamp_worker;
Kernel::KEvent* m_local_clock_event{};
Kernel::KEvent* m_network_clock_event{};
Kernel::KEvent* m_ephemeral_clock_event{};
Kernel::KEvent* m_standard_user_auto_correct_clock_event{};
Kernel::KEvent* m_timer_steady_clock{};
std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event;
Kernel::KEvent* m_timer_file_system{};
std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event;
AlarmWorker m_alarm_worker;
PmStateChangeHandler m_pm_state_change_handler;
};
} // namespace Service::Glue::Time

View File

@@ -20,7 +20,7 @@ void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
std::shared_ptr<HidFirmwareSettings> firmware_settings =
std::make_shared<HidFirmwareSettings>();
std::make_shared<HidFirmwareSettings>(system);
// TODO: Remove this hack when am is emulated properly.
resource_manager->Initialize();

View File

@@ -65,6 +65,9 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
}
void ServiceContext::CloseEvent(Kernel::KEvent* event) {
if (!event) {
return;
}
event->GetReadableEvent().Close();
event->Close();
}

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/steady_clock.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
@@ -29,7 +31,8 @@
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/mifare_result.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/time/time_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
@@ -393,8 +396,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
return result;
}
Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data,
Result NfcDevice::SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data,
std::span<u8> out_data) {
// Not implemented
return ResultSuccess;
@@ -441,7 +443,10 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
if (!is_corrupted && mount_target != NFP::MountTarget::Rom) {
const bool create_backup =
mount_target == NFP::MountTarget::All || mount_target == NFP::MountTarget::Ram ||
(mount_target == NFP::MountTarget::Rom && HasBackup(encrypted_tag_data.uuid).IsError());
if (!is_corrupted && create_backup) {
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
WriteBackupData(encrypted_tag_data.uuid, data);
@@ -1396,27 +1401,41 @@ void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings,
}
NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const {
const auto& time_zone_manager =
system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
Time::TimeZone::CalendarInfo calendar_info{};
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
static_service->GetTimeZoneService(timezone_service);
Service::PSC::Time::CalendarTime calendar_time{};
Service::PSC::Time::CalendarAdditionalInfo additional_info{};
NFP::AmiiboDate amiibo_date{};
amiibo_date.SetYear(2000);
amiibo_date.SetMonth(1);
amiibo_date.SetDay(1);
if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) {
amiibo_date.SetYear(calendar_info.time.year);
amiibo_date.SetMonth(calendar_info.time.month);
amiibo_date.SetDay(calendar_info.time.day);
if (timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time) ==
ResultSuccess) {
amiibo_date.SetYear(calendar_time.year);
amiibo_date.SetMonth(calendar_time.month);
amiibo_date.SetDay(calendar_time.day);
}
return amiibo_date;
}
u64 NfcDevice::GetCurrentPosixTime() const {
auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
return standard_steady_clock.GetCurrentTimePoint(system).time_point;
s64 NfcDevice::GetCurrentPosixTime() const {
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
static_service->GetStandardSteadyClock(steady_clock);
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
return time_point.time_point;
}
u64 NfcDevice::RemoveVersionByte(u64 application_id) const {

View File

@@ -11,7 +11,6 @@
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
namespace Kernel {
class KEvent;
@@ -49,8 +48,8 @@ public:
Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
Result SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data,
std::span<u8> out_data);
Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target);
Result Unmount();
@@ -108,7 +107,7 @@ private:
NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const;
NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 GetCurrentPosixTime() const;
s64 GetCurrentPosixTime() const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();

View File

@@ -6,12 +6,14 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/time/clock_types.h"
#include "core/hle/service/time/time_manager.h"
#include "core/hle/service/psc/time/steady_clock.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/hid_types.h"
#include "hid_core/hid_util.h"
@@ -82,11 +84,19 @@ Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, std::size_t max
continue;
}
if (skip_fatal_errors) {
constexpr u64 MinimumRecoveryTime = 60;
auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point -
time_since_last_error;
constexpr s64 MinimumRecoveryTime = 60;
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u",
true);
std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
static_service->GetStandardSteadyClock(steady_clock);
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
const s64 elapsed_time = time_point.time_point - time_since_last_error;
if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) {
continue;
}
@@ -250,8 +260,7 @@ Result DeviceManager::WriteMifare(u64 device_handle,
return result;
}
Result DeviceManager::SendCommandByPassThrough(u64 device_handle,
const Time::Clock::TimeSpanType& timeout,
Result DeviceManager::SendCommandByPassThrough(u64 device_handle, const s64& timeout,
std::span<const u8> command_data,
std::span<u8> out_data) {
std::scoped_lock lock{mutex};
@@ -741,8 +750,16 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 ||
operation_result == ResultUnknown115) {
auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point;
auto static_service =
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
static_service->GetStandardSteadyClock(steady_clock);
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
time_since_last_error = time_point.time_point;
}
return operation_result;

View File

@@ -13,7 +13,6 @@
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
#include "hid_core/hid_types.h"
namespace Service::NFC {
@@ -42,7 +41,7 @@ public:
std::span<MifareReadBlockData> read_data);
Result WriteMifare(u64 device_handle,
std::span<const MifareWriteBlockParameter> write_parameters);
Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout,
Result SendCommandByPassThrough(u64 device_handle, const s64& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
// Nfp device manager
@@ -92,7 +91,7 @@ private:
const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
bool is_initialized = false;
u64 time_since_last_error = 0;
s64 time_since_last_error = 0;
mutable std::mutex mutex;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};

View File

@@ -13,7 +13,6 @@
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/time/clock_types.h"
#include "hid_core/hid_types.h"
namespace Service::NFC {
@@ -261,10 +260,10 @@ void NfcInterface::WriteMifare(HLERequestContext& ctx) {
void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto timeout{rp.PopRaw<s64>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
device_handle, timeout, command_data.size());
std::vector<u8> out_data(1);
auto result =

View File

@@ -415,4 +415,4 @@ std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage
return std::nullopt;
}
}
} // namespace Service::NS
} // namespace Service::NS

View File

@@ -5,10 +5,7 @@
#include <optional>
#include "common/common_types.h"
namespace Service::Set {
enum class LanguageCode : u64;
}
#include "core/hle/service/set/system_settings_server.h"
namespace Service::NS {
/// This is nn::ns::detail::ApplicationLanguage

View File

@@ -4,9 +4,13 @@
#include <memory>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/psc.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/psc/time/power_state_service.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/service.h"
namespace Service::PSC {
@@ -76,6 +80,17 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system));
server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system));
auto time = std::make_shared<Time::TimeManager>(system);
server_manager->RegisterNamedService(
"time:m", std::make_shared<Time::ServiceManager>(system, time, server_manager.get()));
server_manager->RegisterNamedService(
"time:su", std::make_shared<Time::StaticService>(
system, Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 1}, time, "time:su"));
server_manager->RegisterNamedService("time:al",
std::make_shared<Time::IAlarmService>(system, time));
ServerManager::RunServer(std::move(server_manager));
}

View File

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

View File

@@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class TimeManager;
enum AlarmType : u32 {
WakeupAlarm = 0,
BackgroundTaskAlarm = 1,
};
struct Alarm : public Common::IntrusiveListBaseNode<Alarm> {
using AlarmList = Common::IntrusiveListBaseTraits<Alarm>::ListType;
Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type);
~Alarm();
Kernel::KReadableEvent& GetEventHandle() {
return m_event->GetReadableEvent();
}
s64 GetAlertTime() const {
return m_alert_time;
}
void SetAlertTime(s64 time) {
m_alert_time = time;
}
u32 GetPriority() const {
return m_priority;
}
void Signal() {
m_event->Signal();
}
Result Lock() {
// TODO
// if (m_lock_service) {
// return m_lock_service->Lock();
// }
R_SUCCEED();
}
KernelHelpers::ServiceContext& m_ctx;
u32 m_priority;
Kernel::KEvent* m_event{};
s64 m_alert_time{};
// TODO
// nn::psc::sf::IPmStateLock* m_lock_service{};
};
class Alarms {
public:
explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
PowerStateRequestManager& power_state_request_manager);
~Alarms();
Kernel::KEvent& GetEvent() {
return *m_event;
}
s64 GetRawTime() {
return m_steady_clock.GetRawTime();
}
Result Enable(Alarm& alarm, s64 time);
void Disable(Alarm& alarm);
void CheckAndSignal();
bool GetClosestAlarm(Alarm** out_alarm);
private:
void Insert(Alarm& alarm);
void Erase(Alarm& alarm);
Result UpdateClosestAndSignal();
Core::System& m_system;
KernelHelpers::ServiceContext m_ctx;
StandardSteadyClockCore& m_steady_clock;
PowerStateRequestManager& m_power_state_request_manager;
Alarm::AlarmList m_alarms;
Kernel::KEvent* m_event{};
Alarm* m_closest_alarm{};
std::mutex m_mutex;
};
class IAlarmService final : public ServiceFramework<IAlarmService> {
public:
explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager);
~IAlarmService() override = default;
private:
void CreateWakeupAlarm(HLERequestContext& ctx);
void CreateBackgroundTaskAlarm(HLERequestContext& ctx);
Core::System& m_system;
Alarms& m_alarms;
};
class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> {
public:
explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type);
~ISteadyClockAlarm() override = default;
private:
void GetAlarmEvent(HLERequestContext& ctx);
void Enable(HLERequestContext& ctx);
void Disable(HLERequestContext& ctx);
void IsEnabled(HLERequestContext& ctx);
KernelHelpers::ServiceContext m_ctx;
Alarms& m_alarms;
Alarm m_alarm;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
namespace Service::PSC::Time {
void ContextWriter::SignalAllNodes() {
std::scoped_lock l{m_mutex};
for (auto& operation : m_operation_events) {
operation.m_event->Signal();
}
}
void ContextWriter::Link(OperationEvent& operation_event) {
std::scoped_lock l{m_mutex};
m_operation_events.push_back(operation_event);
}
LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system,
SharedMemory& shared_memory)
: m_system{system}, m_shared_memory{shared_memory} {}
Result LocalSystemClockContextWriter::Write(SystemClockContext& context) {
if (m_in_use) {
R_SUCCEED_IF(context == m_context);
m_context = context;
} else {
m_context = context;
m_in_use = true;
}
m_shared_memory.SetLocalSystemContext(context);
SignalAllNodes();
R_SUCCEED();
}
NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system,
SharedMemory& shared_memory,
SystemClockCore& system_clock)
: m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {}
Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) {
s64 time{};
[[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time);
if (m_in_use) {
R_SUCCEED_IF(context == m_context);
m_context = context;
} else {
m_context = context;
m_in_use = true;
}
m_shared_memory.SetNetworkSystemContext(context);
SignalAllNodes();
R_SUCCEED();
}
EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter(
Core::System& system)
: m_system{system} {}
Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) {
if (m_in_use) {
R_SUCCEED_IF(context == m_context);
m_context = context;
} else {
m_context = context;
m_in_use = true;
}
SignalAllNodes();
R_SUCCEED();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include "common/common_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/shared_memory.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class ContextWriter {
private:
using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;
public:
virtual ~ContextWriter() = default;
virtual Result Write(SystemClockContext& context) = 0;
void SignalAllNodes();
void Link(OperationEvent& operation_event);
private:
OperationEventList m_operation_events;
std::mutex m_mutex;
};
class LocalSystemClockContextWriter : public ContextWriter {
public:
explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory);
Result Write(SystemClockContext& context) override;
private:
Core::System& m_system;
SharedMemory& m_shared_memory;
bool m_in_use{};
SystemClockContext m_context{};
};
class NetworkSystemClockContextWriter : public ContextWriter {
public:
explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory,
SystemClockCore& system_clock);
Result Write(SystemClockContext& context) override;
private:
Core::System& m_system;
SharedMemory& m_shared_memory;
bool m_in_use{};
SystemClockContext m_context{};
SystemClockCore& m_system_clock;
};
class EphemeralNetworkSystemClockContextWriter : public ContextWriter {
public:
EphemeralNetworkSystemClockContextWriter(Core::System& system);
Result Write(SystemClockContext& context) override;
private:
Core::System& m_system;
bool m_in_use{};
SystemClockContext m_context{};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"
namespace Service::PSC::Time {
class EphemeralNetworkSystemClockCore : public SystemClockCore {
public:
explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock)
: SystemClockCore{steady_clock} {}
~EphemeralNetworkSystemClockCore() override = default;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
namespace Service::PSC::Time {
void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) {
SteadyClockTimePoint time_point{};
if (GetCurrentTimePoint(time_point) == ResultSuccess &&
context.steady_time_point.IdMatches(time_point)) {
SetContextAndWrite(context);
} else if (SetCurrentTime(time) != ResultSuccess) {
LOG_ERROR(Service_Time, "Failed to SetCurrentTime");
}
SetInitialized();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"
namespace Service::PSC::Time {
class StandardLocalSystemClockCore : public SystemClockCore {
public:
explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock)
: SystemClockCore{steady_clock} {}
~StandardLocalSystemClockCore() override = default;
void Initialize(SystemClockContext& context, s64 time);
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
namespace Service::PSC::Time {
void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) {
if (SetContextAndWrite(context) != ResultSuccess) {
LOG_ERROR(Service_Time, "Failed to SetContext");
}
m_sufficient_accuracy = accuracy;
SetInitialized();
}
bool StandardNetworkSystemClockCore::IsAccuracySufficient() {
if (!IsInitialized()) {
return false;
}
SystemClockContext context{};
SteadyClockTimePoint current_time_point{};
if (GetCurrentTimePoint(current_time_point) != ResultSuccess ||
GetContext(context) != ResultSuccess) {
return false;
}
s64 seconds{};
if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) !=
ResultSuccess) {
return false;
}
if (std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(seconds))
.count() < m_sufficient_accuracy) {
return true;
}
return false;
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"
namespace Service::PSC::Time {
class StandardNetworkSystemClockCore : public SystemClockCore {
public:
explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock)
: SystemClockCore{steady_clock} {}
~StandardNetworkSystemClockCore() override = default;
void Initialize(SystemClockContext& context, s64 accuracy);
bool IsAccuracySufficient();
private:
s64 m_sufficient_accuracy{
std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
namespace Service::PSC::Time {
void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset,
s64 internal_offset, s64 test_offset,
bool is_rtc_reset_detected) {
m_clock_source_id = clock_source_id;
m_rtc_offset = rtc_offset;
m_internal_offset = internal_offset;
m_test_offset = test_offset;
if (is_rtc_reset_detected) {
SetResetDetected();
}
SetInitialized();
}
void StandardSteadyClockCore::SetRtcOffset(s64 offset) {
m_rtc_offset = offset;
}
void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) {
auto ticks{m_system.CoreTiming().GetClockTicks()};
m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count();
m_continuous_adjustment_time_point.diff_scale = 0;
m_continuous_adjustment_time_point.shift_amount = 0;
m_continuous_adjustment_time_point.lower = time;
m_continuous_adjustment_time_point.upper = time;
m_continuous_adjustment_time_point.clock_source_id = clock_source_id;
}
void StandardSteadyClockCore::GetContinuousAdjustment(
ContinuousAdjustmentTimePoint& out_time_point) const {
out_time_point = m_continuous_adjustment_time_point;
}
void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) {
auto ticks{m_system.CoreTiming().GetClockTicks()};
auto uptime_ns{ConvertToTimeSpan(ticks).count()};
auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) *
m_continuous_adjustment_time_point.diff_scale) >>
m_continuous_adjustment_time_point.shift_amount};
auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower};
auto last_time_point{m_continuous_adjustment_time_point.upper};
m_continuous_adjustment_time_point.upper = in_time;
auto t1{std::min<s64>(expected_time, last_time_point)};
expected_time = std::max<s64>(expected_time, last_time_point);
expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time;
auto new_diff{in_time < expected_time ? -55 : 55};
m_continuous_adjustment_time_point.rtc_offset = uptime_ns;
m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14;
m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff;
m_continuous_adjustment_time_point.lower = expected_time;
}
Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
auto current_time_ns = GetCurrentRawTimePointImpl();
auto current_time_s =
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns));
out_time_point.time_point = current_time_s.count();
out_time_point.clock_source_id = m_clock_source_id;
R_SUCCEED();
}
s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() {
std::scoped_lock l{m_mutex};
auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())};
auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count();
auto time_point = std::max<s64>(current_time_ns, m_cached_time_point);
m_cached_time_point = time_point;
return time_point;
}
s64 StandardSteadyClockCore::GetTestOffsetImpl() const {
return m_test_offset;
}
void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) {
m_test_offset = offset;
}
s64 StandardSteadyClockCore::GetInternalOffsetImpl() const {
return m_internal_offset;
}
void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) {
m_internal_offset = offset;
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class StandardSteadyClockCore : public SteadyClockCore {
public:
explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {}
~StandardSteadyClockCore() override = default;
void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset,
s64 test_offset, bool is_rtc_reset_detected);
void SetRtcOffset(s64 offset);
void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time);
void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const;
void UpdateContinuousAdjustmentTime(s64 time);
Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
s64 GetCurrentRawTimePointImpl() override;
s64 GetTestOffsetImpl() const override;
void SetTestOffsetImpl(s64 offset) override;
s64 GetInternalOffsetImpl() const override;
void SetInternalOffsetImpl(s64 offset) override;
Result GetRtcValueImpl(s64& out_value) override {
R_RETURN(ResultNotImplemented);
}
Result GetSetupResultValueImpl() override {
R_SUCCEED();
}
private:
Core::System& m_system;
std::mutex m_mutex;
s64 m_test_offset{};
s64 m_internal_offset{};
ClockSourceId m_clock_source_id{};
s64 m_rtc_offset{};
s64 m_cached_time_point{};
ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
namespace Service::PSC::Time {
StandardUserSystemClockCore::StandardUserSystemClockCore(
Core::System& system, StandardLocalSystemClockCore& local_clock,
StandardNetworkSystemClockCore& network_clock)
: SystemClockCore{local_clock.GetSteadyClock()}, m_system{system},
m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock},
m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent(
"Psc:StandardUserSystemClockCore:Event")} {}
StandardUserSystemClockCore::~StandardUserSystemClockCore() {
m_ctx.CloseEvent(m_event);
}
Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) {
R_SUCCEED_IF(m_automatic_correction == automatic_correction);
R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches());
SystemClockContext context{};
R_TRY(m_network_system_clock.GetContext(context));
R_TRY(m_local_system_clock.SetContextAndWrite(context));
m_automatic_correction = automatic_correction;
R_SUCCEED();
}
Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const {
if (!m_automatic_correction) {
R_RETURN(m_local_system_clock.GetContext(out_context));
}
if (!m_network_system_clock.CheckClockSourceMatches()) {
R_RETURN(m_local_system_clock.GetContext(out_context));
}
SystemClockContext context{};
R_TRY(m_network_system_clock.GetContext(context));
R_TRY(m_local_system_clock.SetContextAndWrite(context));
R_RETURN(m_local_system_clock.GetContext(out_context));
}
Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) {
R_RETURN(ResultNotImplemented);
}
Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) {
out_time_point = m_time_point;
R_SUCCEED();
}
void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) {
m_time_point = time_point;
m_event->Signal();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_event.h"
#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class StandardUserSystemClockCore : public SystemClockCore {
public:
explicit StandardUserSystemClockCore(Core::System& system,
StandardLocalSystemClockCore& local_clock,
StandardNetworkSystemClockCore& network_clock);
~StandardUserSystemClockCore() override;
Kernel::KEvent& GetEvent() {
return *m_event;
}
bool GetAutomaticCorrection() const {
return m_automatic_correction;
}
Result SetAutomaticCorrection(bool automatic_correction);
Result GetContext(SystemClockContext& out_context) const override;
Result SetContext(SystemClockContext& context) override;
Result GetTimePoint(SteadyClockTimePoint& out_time_point);
void SetTimePointAndSignal(SteadyClockTimePoint& time_point);
private:
Core::System& m_system;
KernelHelpers::ServiceContext m_ctx;
bool m_automatic_correction{};
StandardLocalSystemClockCore& m_local_system_clock;
StandardNetworkSystemClockCore& m_network_system_clock;
SteadyClockTimePoint m_time_point{};
Kernel::KEvent* m_event{};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "core/hle/result.h"
#include "core/hle/service/psc/time/common.h"
namespace Service::PSC::Time {
class SteadyClockCore {
public:
SteadyClockCore() = default;
virtual ~SteadyClockCore() = default;
void SetInitialized() {
m_initialized = true;
}
bool IsInitialized() const {
return m_initialized;
}
void SetResetDetected() {
m_reset_detected = true;
}
bool IsResetDetected() const {
return m_reset_detected;
}
Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
R_TRY(GetCurrentTimePointImpl(out_time_point));
auto one_second_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
out_time_point.time_point += GetTestOffsetImpl() / one_second_ns;
out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns;
R_SUCCEED();
}
s64 GetTestOffset() const {
return GetTestOffsetImpl();
}
void SetTestOffset(s64 offset) {
SetTestOffsetImpl(offset);
}
s64 GetInternalOffset() const {
return GetInternalOffsetImpl();
}
s64 GetRawTime() {
return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl();
}
Result GetRtcValue(s64& out_value) {
R_RETURN(GetRtcValueImpl(out_value));
}
Result GetSetupResultValue() {
R_RETURN(GetSetupResultValueImpl());
}
private:
virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0;
virtual s64 GetCurrentRawTimePointImpl() = 0;
virtual s64 GetTestOffsetImpl() const = 0;
virtual void SetTestOffsetImpl(s64 offset) = 0;
virtual s64 GetInternalOffsetImpl() const = 0;
virtual void SetInternalOffsetImpl(s64 offset) = 0;
virtual Result GetRtcValueImpl(s64& out_value) = 0;
virtual Result GetSetupResultValueImpl() = 0;
bool m_initialized{};
bool m_reset_detected{};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
namespace Service::PSC::Time {
bool SystemClockCore::CheckClockSourceMatches() {
SystemClockContext context{};
if (GetContext(context) != ResultSuccess) {
return false;
}
SteadyClockTimePoint time_point{};
if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) {
return false;
}
return context.steady_time_point.IdMatches(time_point);
}
Result SystemClockCore::GetCurrentTime(s64* out_time) const {
R_UNLESS(out_time != nullptr, ResultInvalidArgument);
SystemClockContext context{};
SteadyClockTimePoint time_point{};
R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
R_TRY(GetContext(context));
R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch);
*out_time = context.offset + time_point.time_point;
R_SUCCEED();
}
Result SystemClockCore::SetCurrentTime(s64 time) {
SteadyClockTimePoint time_point{};
R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
SystemClockContext context{
.offset = time - time_point.time_point,
.steady_time_point = time_point,
};
R_RETURN(SetContextAndWrite(context));
}
Result SystemClockCore::GetContext(SystemClockContext& out_context) const {
out_context = m_context;
R_SUCCEED();
}
Result SystemClockCore::SetContext(SystemClockContext& context) {
m_context = context;
R_SUCCEED();
}
Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) {
R_TRY(SetContext(context));
if (m_context_writer) {
R_RETURN(m_context_writer->Write(context));
}
R_SUCCEED();
}
void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) {
if (m_context_writer) {
m_context_writer->Link(operation_event);
}
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/common.h"
namespace Service::PSC::Time {
class ContextWriter;
class SystemClockCore {
public:
explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {}
virtual ~SystemClockCore() = default;
SteadyClockCore& GetSteadyClock() {
return m_steady_clock;
}
bool IsInitialized() const {
return m_initialized;
}
void SetInitialized() {
m_initialized = true;
}
void SetContextWriter(ContextWriter& context_writer) {
m_context_writer = &context_writer;
}
bool CheckClockSourceMatches();
Result GetCurrentTime(s64* out_time) const;
Result SetCurrentTime(s64 time);
Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point));
}
virtual Result GetContext(SystemClockContext& out_context) const;
virtual Result SetContext(SystemClockContext& context);
Result SetContextAndWrite(SystemClockContext& context);
void LinkOperationEvent(OperationEvent& operation_event);
private:
bool m_initialized{};
ContextWriter* m_context_writer{};
SteadyClockCore& m_steady_clock;
SystemClockContext m_context{};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
namespace Service::PSC::Time {
Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
auto ticks{m_system.CoreTiming().GetClockTicks()};
auto current_time_s =
std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count();
out_time_point.time_point = current_time_s;
out_time_point.clock_source_id = m_clock_source_id;
R_SUCCEED();
}
s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() {
SteadyClockTimePoint time_point{};
if (GetCurrentTimePointImpl(time_point) != ResultSuccess) {
LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!");
}
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::seconds(time_point.time_point))
.count();
}
s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const {
return 0;
}
void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {}
s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const {
return 0;
}
void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include "common/uuid.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class TickBasedSteadyClockCore : public SteadyClockCore {
public:
explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {}
~TickBasedSteadyClockCore() override = default;
Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
s64 GetCurrentRawTimePointImpl() override;
s64 GetTestOffsetImpl() const override;
void SetTestOffsetImpl(s64 offset) override;
s64 GetInternalOffsetImpl() const override;
void SetInternalOffsetImpl(s64 offset) override;
Result GetRtcValueImpl(s64& out_value) override {
R_RETURN(ResultNotImplemented);
}
Result GetSetupResultValueImpl() override {
R_SUCCEED();
}
private:
Core::System& m_system;
ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/psc/time/common.h"
namespace Service::PSC::Time {
OperationEvent::OperationEvent(Core::System& system)
: m_ctx{system, "Time:OperationEvent"}, m_event{
m_ctx.CreateEvent("Time:OperationEvent:Event")} {}
OperationEvent::~OperationEvent() {
m_ctx.CloseEvent(m_event);
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,168 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <chrono>
#include "common/common_types.h"
#include "common/intrusive_list.h"
#include "common/uuid.h"
#include "common/wall_clock.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/errors.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
using ClockSourceId = Common::UUID;
struct SteadyClockTimePoint {
constexpr bool IdMatches(SteadyClockTimePoint& other) {
return clock_source_id == other.clock_source_id;
}
bool operator==(const SteadyClockTimePoint& other) const = default;
s64 time_point;
ClockSourceId clock_source_id;
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint has the wrong size!");
static_assert(std::is_trivial_v<ClockSourceId>);
struct SystemClockContext {
bool operator==(const SystemClockContext& other) const = default;
s64 offset;
SteadyClockTimePoint steady_time_point;
};
static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!");
static_assert(std::is_trivial_v<SystemClockContext>);
enum class TimeType : u8 {
UserSystemClock,
NetworkSystemClock,
LocalSystemClock,
};
struct CalendarTime {
s16 year;
s8 month;
s8 day;
s8 hour;
s8 minute;
s8 second;
};
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime has the wrong size!");
struct CalendarAdditionalInfo {
s32 day_of_week;
s32 day_of_year;
std::array<char, 8> name;
s32 is_dst;
s32 ut_offset;
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!");
struct LocationName {
std::array<char, 36> name;
};
static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!");
struct RuleVersion {
std::array<char, 16> version;
};
static_assert(sizeof(RuleVersion) == 0x10, "RuleVersion has the wrong size!");
struct ClockSnapshot {
SystemClockContext user_context;
SystemClockContext network_context;
s64 user_time;
s64 network_time;
CalendarTime user_calendar_time;
CalendarTime network_calendar_time;
CalendarAdditionalInfo user_calendar_additional_time;
CalendarAdditionalInfo network_calendar_additional_time;
SteadyClockTimePoint steady_clock_time_point;
LocationName location_name;
bool is_automatic_correction_enabled;
TimeType type;
u16 unk_CE;
};
static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot has the wrong size!");
static_assert(std::is_trivial_v<ClockSnapshot>);
struct ContinuousAdjustmentTimePoint {
s64 rtc_offset;
s64 diff_scale;
s64 shift_amount;
s64 lower;
s64 upper;
ClockSourceId clock_source_id;
};
static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38,
"ContinuousAdjustmentTimePoint has the wrong size!");
static_assert(std::is_trivial_v<ContinuousAdjustmentTimePoint>);
struct AlarmInfo {
s64 alert_time;
u32 priority;
};
static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!");
struct StaticServiceSetupInfo {
bool can_write_local_clock;
bool can_write_user_clock;
bool can_write_network_clock;
bool can_write_timezone_device_location;
bool can_write_steady_clock;
bool can_write_uninitialized_clock;
};
static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!");
struct OperationEvent : public Common::IntrusiveListBaseNode<OperationEvent> {
using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;
OperationEvent(Core::System& system);
~OperationEvent();
KernelHelpers::ServiceContext m_ctx;
Kernel::KEvent* m_event{};
};
constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) {
constexpr auto one_second_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
constexpr s64 max{Common::WallClock::CNTFRQ *
(std::numeric_limits<s64>::max() / one_second_ns)};
if (ticks > max) {
return std::chrono::nanoseconds(std::numeric_limits<s64>::max());
} else if (ticks < -max) {
return std::chrono::nanoseconds(std::numeric_limits<s64>::min());
}
auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns};
auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ};
return std::chrono::nanoseconds(a + b);
}
constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, SteadyClockTimePoint& a,
SteadyClockTimePoint& b) {
R_UNLESS(out_seconds, ResultInvalidArgument);
R_UNLESS(a.IdMatches(b), ResultInvalidArgument);
R_UNLESS(a.time_point >= 0 || b.time_point <= a.time_point + std::numeric_limits<s64>::max(),
ResultOverflow);
R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits<s64>::min(),
ResultOverflow);
*out_seconds = b.time_point - a.time_point;
R_SUCCEED();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::PSC::Time {
constexpr Result ResultPermissionDenied{ErrorModule::Time, 1};
constexpr Result ResultClockMismatch{ErrorModule::Time, 102};
constexpr Result ResultClockUninitialized{ErrorModule::Time, 103};
constexpr Result ResultTimeNotFound{ErrorModule::Time, 200};
constexpr Result ResultOverflow{ErrorModule::Time, 201};
constexpr Result ResultFailed{ErrorModule::Time, 801};
constexpr Result ResultInvalidArgument{ErrorModule::Time, 901};
constexpr Result ResultTimeZoneOutOfRange{ErrorModule::Time, 902};
constexpr Result ResultTimeZoneParseFailed{ErrorModule::Time, 903};
constexpr Result ResultRtcTimeout{ErrorModule::Time, 988};
constexpr Result ResultTimeZoneNotFound{ErrorModule::Time, 989};
constexpr Result ResultNotImplemented{ErrorModule::Time, 990};
constexpr Result ResultAlarmNotRegistered{ErrorModule::Time, 1502};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/psc/time/alarms.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"
#include "core/hle/service/psc/time/shared_memory.h"
#include "core/hle/service/psc/time/time_zone.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class TimeManager {
public:
explicit TimeManager(Core::System& system)
: m_system{system}, m_standard_steady_clock{system}, m_tick_based_steady_clock{m_system},
m_standard_local_system_clock{m_standard_steady_clock},
m_standard_network_system_clock{m_standard_steady_clock},
m_standard_user_system_clock{m_system, m_standard_local_system_clock,
m_standard_network_system_clock},
m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system},
m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock,
m_power_state_request_manager},
m_local_system_clock_context_writer{m_system, m_shared_memory},
m_network_system_clock_context_writer{m_system, m_shared_memory,
m_standard_user_system_clock},
m_ephemeral_network_clock_context_writer{m_system} {}
Core::System& m_system;
StandardSteadyClockCore m_standard_steady_clock;
TickBasedSteadyClockCore m_tick_based_steady_clock;
StandardLocalSystemClockCore m_standard_local_system_clock;
StandardNetworkSystemClockCore m_standard_network_system_clock;
StandardUserSystemClockCore m_standard_user_system_clock;
EphemeralNetworkSystemClockCore m_ephemeral_network_clock;
TimeZone m_time_zone;
SharedMemory m_shared_memory;
PowerStateRequestManager m_power_state_request_manager;
Alarms m_alarms;
LocalSystemClockContextWriter m_local_system_clock_context_writer;
NetworkSystemClockContextWriter m_network_system_clock_context_writer;
EphemeralNetworkSystemClockContextWriter m_ephemeral_network_clock_context_writer;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"
namespace Service::PSC::Time {
PowerStateRequestManager::PowerStateRequestManager(Core::System& system)
: m_system{system}, m_ctx{system, "Psc:PowerStateRequestManager"},
m_event{m_ctx.CreateEvent("Psc:PowerStateRequestManager:Event")} {}
PowerStateRequestManager::~PowerStateRequestManager() {
m_ctx.CloseEvent(m_event);
}
void PowerStateRequestManager::UpdatePendingPowerStateRequestPriority(u32 priority) {
std::scoped_lock l{m_mutex};
if (m_has_pending_request) {
m_pending_request_priority = std::max(m_pending_request_priority, priority);
} else {
m_pending_request_priority = priority;
m_has_pending_request = true;
}
}
void PowerStateRequestManager::SignalPowerStateRequestAvailability() {
std::scoped_lock l{m_mutex};
if (m_has_pending_request) {
if (!m_has_available_request) {
m_has_available_request = true;
}
m_has_pending_request = false;
m_available_request_priority = m_pending_request_priority;
m_event->Signal();
}
}
bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) {
std::scoped_lock l{m_mutex};
auto had_request{m_has_available_request};
if (m_has_available_request) {
out_priority = m_available_request_priority;
m_has_available_request = false;
m_event->Clear();
}
return had_request;
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class PowerStateRequestManager {
public:
explicit PowerStateRequestManager(Core::System& system);
~PowerStateRequestManager();
Kernel::KReadableEvent& GetReadableEvent() {
return m_event->GetReadableEvent();
}
void UpdatePendingPowerStateRequestPriority(u32 priority);
void SignalPowerStateRequestAvailability();
bool GetAndClearPowerStateRequest(u32& out_priority);
private:
Core::System& m_system;
KernelHelpers::ServiceContext m_ctx;
Kernel::KEvent* m_event{};
bool m_has_pending_request{};
u32 m_pending_request_priority{};
bool m_has_available_request{};
u32 m_available_request_priority{};
std::mutex m_mutex;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/psc/time/power_state_service.h"
namespace Service::PSC::Time {
IPowerStateRequestHandler::IPowerStateRequestHandler(
Core::System& system_, PowerStateRequestManager& power_state_request_manager)
: ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{
power_state_request_manager} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle, "GetPowerStateRequestEventReadableHandle"},
{1, &IPowerStateRequestHandler::GetAndClearPowerStateRequest, "GetAndClearPowerStateRequest"},
};
// clang-format on
RegisterHandlers(functions);
}
void IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(m_power_state_request_manager.GetReadableEvent());
}
void IPowerStateRequestHandler::GetAndClearPowerStateRequest(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
u32 priority{};
auto cleared = m_power_state_request_manager.GetAndClearPowerStateRequest(priority);
if (cleared) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(priority);
rb.Push(cleared);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(cleared);
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> {
public:
explicit IPowerStateRequestHandler(Core::System& system,
PowerStateRequestManager& power_state_request_manager);
~IPowerStateRequestHandler() override = default;
private:
void GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx);
void GetAndClearPowerStateRequest(HLERequestContext& ctx);
Core::System& m_system;
PowerStateRequestManager& m_power_state_request_manager;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,494 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/psc/time/power_state_service.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
namespace Service::PSC::Time {
ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time,
ServerManager* server_manager)
: ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)},
m_server_manager{*server_manager},
m_local_system_clock{m_time->m_standard_local_system_clock},
m_user_system_clock{m_time->m_standard_user_system_clock},
m_network_system_clock{m_time->m_standard_network_system_clock},
m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone},
m_ephemeral_network_clock{m_time->m_ephemeral_network_clock},
m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms},
m_local_system_context_writer{m_time->m_local_system_clock_context_writer},
m_network_system_context_writer{m_time->m_network_system_clock_context_writer},
m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer},
m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ServiceManager::Handle_GetStaticServiceAsUser, "GetStaticServiceAsUser"},
{5, &ServiceManager::Handle_GetStaticServiceAsAdmin, "GetStaticServiceAsAdmin"},
{6, &ServiceManager::Handle_GetStaticServiceAsRepair, "GetStaticServiceAsRepair"},
{9, &ServiceManager::Handle_GetStaticServiceAsServiceManager, "GetStaticServiceAsServiceManager"},
{10, &ServiceManager::Handle_SetupStandardSteadyClockCore, "SetupStandardSteadyClockCore"},
{11, &ServiceManager::Handle_SetupStandardLocalSystemClockCore, "SetupStandardLocalSystemClockCore"},
{12, &ServiceManager::Handle_SetupStandardNetworkSystemClockCore, "SetupStandardNetworkSystemClockCore"},
{13, &ServiceManager::Handle_SetupStandardUserSystemClockCore, "SetupStandardUserSystemClockCore"},
{14, &ServiceManager::Handle_SetupTimeZoneServiceCore, "SetupTimeZoneServiceCore"},
{15, &ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore, "SetupEphemeralNetworkSystemClockCore"},
{50, &ServiceManager::Handle_GetStandardLocalClockOperationEvent, "GetStandardLocalClockOperationEvent"},
{51, &ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager, "GetStandardNetworkClockOperationEventForServiceManager"},
{52, &ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager, "GetEphemeralNetworkClockOperationEventForServiceManager"},
{60, &ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent, "GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent"},
{100, &ServiceManager::Handle_SetStandardSteadyClockBaseTime, "SetStandardSteadyClockBaseTime"},
{200, &ServiceManager::Handle_GetClosestAlarmUpdatedEvent, "GetClosestAlarmUpdatedEvent"},
{201, &ServiceManager::Handle_CheckAndSignalAlarms, "CheckAndSignalAlarms"},
{202, &ServiceManager::Handle_GetClosestAlarmInfo, "GetClosestAlarmInfo "},
};
// clang-format on
RegisterHandlers(functions);
m_local_system_context_writer.Link(m_local_operation);
m_network_system_context_writer.Link(m_network_operation);
m_ephemeral_system_context_writer.Link(m_ephemeral_operation);
}
void ServiceManager::SetupSAndP() {
if (!m_is_s_and_p_setup) {
m_is_s_and_p_setup = true;
m_server_manager.RegisterNamedService(
"time:s", std::make_shared<StaticService>(
m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
m_server_manager.RegisterNamedService("time:p",
std::make_shared<IPowerStateRequestHandler>(
m_system, m_time->m_power_state_request_manager));
}
}
void ServiceManager::CheckAndSetupServicesSAndP() {
if (m_local_system_clock.IsInitialized() && m_user_system_clock.IsInitialized() &&
m_network_system_clock.IsInitialized() && m_steady_clock.IsInitialized() &&
m_time_zone.IsInitialized() && m_ephemeral_network_clock.IsInitialized()) {
SetupSAndP();
}
}
void ServiceManager::Handle_GetStaticServiceAsUser(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<StaticService> service{};
auto res = GetStaticServiceAsUser(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<StaticService>(std::move(service));
}
void ServiceManager::Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<StaticService> service{};
auto res = GetStaticServiceAsAdmin(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<StaticService>(std::move(service));
}
void ServiceManager::Handle_GetStaticServiceAsRepair(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<StaticService> service{};
auto res = GetStaticServiceAsRepair(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<StaticService>(std::move(service));
}
void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<StaticService> service{};
auto res = GetStaticServiceAsServiceManager(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<StaticService>(std::move(service));
}
void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
struct Parameters {
bool reset_detected;
Common::UUID clock_source_id;
s64 rtc_offset;
s64 internal_offset;
s64 test_offset;
};
static_assert(sizeof(Parameters) == 0x30);
IPC::RequestParser rp{ctx};
auto params{rp.PopRaw<Parameters>()};
auto res = SetupStandardSteadyClockCore(params.clock_source_id, params.rtc_offset,
params.internal_offset, params.test_offset,
params.reset_detected);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto context{rp.PopRaw<SystemClockContext>()};
auto time{rp.Pop<s64>()};
auto res = SetupStandardLocalSystemClockCore(context, time);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto context{rp.PopRaw<SystemClockContext>()};
auto accuracy{rp.Pop<s64>()};
auto res = SetupStandardNetworkSystemClockCore(context, accuracy);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
struct Parameters {
bool automatic_correction;
SteadyClockTimePoint time_point;
};
static_assert(sizeof(Parameters) == 0x20);
IPC::RequestParser rp{ctx};
auto params{rp.PopRaw<Parameters>()};
auto res = SetupStandardUserSystemClockCore(params.time_point, params.automatic_correction);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
struct Parameters {
u32 location_count;
LocationName name;
SteadyClockTimePoint time_point;
RuleVersion rule_version;
};
static_assert(sizeof(Parameters) == 0x50);
IPC::RequestParser rp{ctx};
auto params{rp.PopRaw<Parameters>()};
auto rule_buffer{ctx.ReadBuffer()};
auto res = SetupTimeZoneServiceCore(params.name, params.time_point, params.rule_version,
params.location_count, rule_buffer);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
auto res = SetupEphemeralNetworkSystemClockCore();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KEvent* event{};
auto res = GetStandardLocalClockOperationEvent(&event);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(event->GetReadableEvent());
}
void ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KEvent* event{};
auto res = GetStandardNetworkClockOperationEventForServiceManager(&event);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(event);
}
void ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KEvent* event{};
auto res = GetEphemeralNetworkClockOperationEventForServiceManager(&event);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(event);
}
void ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KEvent* event{};
auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(&event);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(event);
}
void ServiceManager::Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto base_time{rp.Pop<s64>()};
auto res = SetStandardSteadyClockBaseTime(base_time);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KEvent* event{};
auto res = GetClosestAlarmUpdatedEvent(&event);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.PushCopyObjects(event->GetReadableEvent());
}
void ServiceManager::Handle_CheckAndSignalAlarms(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
auto res = CheckAndSignalAlarms();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void ServiceManager::Handle_GetClosestAlarmInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
AlarmInfo alarm_info{};
bool is_valid{};
s64 time{};
auto res = GetClosestAlarmInfo(is_valid, alarm_info, time);
struct OutParameters {
bool is_valid;
AlarmInfo alarm_info;
s64 time;
};
static_assert(sizeof(OutParameters) == 0x20);
OutParameters out_params{
.is_valid = is_valid,
.alarm_info = alarm_info,
.time = time,
};
IPC::ResponseBuilder rb{ctx, 2 + sizeof(OutParameters) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<OutParameters>(out_params);
}
// =============================== Implementations ===========================
Result ServiceManager::GetStaticService(std::shared_ptr<StaticService>& out_service,
StaticServiceSetupInfo setup_info, const char* name) {
out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name);
R_SUCCEED();
}
Result ServiceManager::GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service) {
R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u"));
}
Result ServiceManager::GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service) {
R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a"));
}
Result ServiceManager::GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service) {
R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r"));
}
Result ServiceManager::GetStaticServiceAsServiceManager(
std::shared_ptr<StaticService>& out_service) {
R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 1, 1, 1, 0}, "time:sm"));
}
Result ServiceManager::SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset,
s64 internal_offset, s64 test_offset,
bool is_rtc_reset_detected) {
m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset,
is_rtc_reset_detected);
auto time = m_steady_clock.GetRawTime();
auto ticks = m_system.CoreTiming().GetClockTicks();
auto boot_time = time - ConvertToTimeSpan(ticks).count();
m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time);
m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time);
ContinuousAdjustmentTimePoint time_point{};
m_steady_clock.GetContinuousAdjustment(time_point);
m_shared_memory.SetContinuousAdjustment(time_point);
CheckAndSetupServicesSAndP();
R_SUCCEED();
}
Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) {
m_local_system_clock.SetContextWriter(m_local_system_context_writer);
m_local_system_clock.Initialize(context, time);
CheckAndSetupServicesSAndP();
R_SUCCEED();
}
Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context,
s64 accuracy) {
// TODO this is a hack! The network clock should be updated independently, from the ntc service
// and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
// to avoid it being stuck at 0.
if (context == Service::PSC::Time::SystemClockContext{}) {
m_local_system_clock.GetContext(context);
}
m_network_system_clock.SetContextWriter(m_network_system_context_writer);
m_network_system_clock.Initialize(context, accuracy);
CheckAndSetupServicesSAndP();
R_SUCCEED();
}
Result ServiceManager::SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point,
bool automatic_correction) {
// TODO this is a hack! The user clock should be updated independently, from the ntc service
// and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
// to avoid it being stuck at 0.
if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) {
m_local_system_clock.GetCurrentTimePoint(time_point);
}
m_user_system_clock.SetAutomaticCorrection(automatic_correction);
m_user_system_clock.SetTimePointAndSignal(time_point);
m_user_system_clock.SetInitialized();
m_shared_memory.SetAutomaticCorrection(automatic_correction);
CheckAndSetupServicesSAndP();
R_SUCCEED();
}
Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name,
SteadyClockTimePoint& time_point,
RuleVersion& rule_version, u32 location_count,
std::span<const u8> rule_buffer) {
if (m_time_zone.ParseBinary(name, rule_buffer) != ResultSuccess) {
LOG_ERROR(Service_Time, "Failed to parse time zone binary!");
}
m_time_zone.SetTimePoint(time_point);
m_time_zone.SetTotalLocationNameCount(location_count);
m_time_zone.SetRuleVersion(rule_version);
m_time_zone.SetInitialized();
CheckAndSetupServicesSAndP();
R_SUCCEED();
}
Result ServiceManager::SetupEphemeralNetworkSystemClockCore() {
m_ephemeral_network_clock.SetContextWriter(m_ephemeral_system_context_writer);
m_ephemeral_network_clock.SetInitialized();
CheckAndSetupServicesSAndP();
R_SUCCEED();
}
Result ServiceManager::GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event) {
*out_event = m_local_operation.m_event;
R_SUCCEED();
}
Result ServiceManager::GetStandardNetworkClockOperationEventForServiceManager(
Kernel::KEvent** out_event) {
*out_event = m_network_operation.m_event;
R_SUCCEED();
}
Result ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager(
Kernel::KEvent** out_event) {
*out_event = m_ephemeral_operation.m_event;
R_SUCCEED();
}
Result ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
Kernel::KEvent** out_event) {
*out_event = &m_user_system_clock.GetEvent();
R_SUCCEED();
}
Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) {
m_steady_clock.SetRtcOffset(base_time);
auto time = m_steady_clock.GetRawTime();
auto ticks = m_system.CoreTiming().GetClockTicks();
auto diff = time - ConvertToTimeSpan(ticks).count();
m_shared_memory.UpdateBaseTime(diff);
m_steady_clock.UpdateContinuousAdjustmentTime(diff);
ContinuousAdjustmentTimePoint time_point{};
m_steady_clock.GetContinuousAdjustment(time_point);
m_shared_memory.SetContinuousAdjustment(time_point);
R_SUCCEED();
}
Result ServiceManager::GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event) {
*out_event = &m_alarms.GetEvent();
R_SUCCEED();
}
Result ServiceManager::CheckAndSignalAlarms() {
m_alarms.CheckAndSignal();
R_SUCCEED();
}
Result ServiceManager::GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time) {
Alarm* alarm{nullptr};
out_is_valid = m_alarms.GetClosestAlarm(&alarm);
if (out_is_valid) {
out_info = {
.alert_time = alarm->GetAlertTime(),
.priority = alarm->GetPriority(),
};
out_time = m_alarms.GetRawTime();
}
R_SUCCEED();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <memory>
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KReadableEvent;
}
namespace Service::PSC::Time {
class StaticService;
class ServiceManager final : public ServiceFramework<ServiceManager> {
public:
explicit ServiceManager(Core::System& system, std::shared_ptr<TimeManager> time,
ServerManager* server_manager);
~ServiceManager() override = default;
Result GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service);
Result GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service);
Result GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service);
Result GetStaticServiceAsServiceManager(std::shared_ptr<StaticService>& out_service);
Result SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset,
s64 internal_offset, s64 test_offset,
bool is_rtc_reset_detected);
Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time);
Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy);
Result SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point,
bool automatic_correction);
Result SetupTimeZoneServiceCore(LocationName& name, SteadyClockTimePoint& time_point,
RuleVersion& rule_version, u32 location_count,
std::span<const u8> rule_buffer);
Result SetupEphemeralNetworkSystemClockCore();
Result GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event);
Result GetStandardNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event);
Result GetEphemeralNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event);
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(Kernel::KEvent** out_event);
Result SetStandardSteadyClockBaseTime(s64 base_time);
Result GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event);
Result CheckAndSignalAlarms();
Result GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time);
private:
void CheckAndSetupServicesSAndP();
void SetupSAndP();
Result GetStaticService(std::shared_ptr<StaticService>& out_service,
StaticServiceSetupInfo setup_info, const char* name);
void Handle_GetStaticServiceAsUser(HLERequestContext& ctx);
void Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx);
void Handle_GetStaticServiceAsRepair(HLERequestContext& ctx);
void Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx);
void Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx);
void Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx);
void Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx);
void Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx);
void Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx);
void Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx);
void Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx);
void Handle_GetStandardNetworkClockOperationEventForServiceManager(HLERequestContext& ctx);
void Handle_GetEphemeralNetworkClockOperationEventForServiceManager(HLERequestContext& ctx);
void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(HLERequestContext& ctx);
void Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx);
void Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx);
void Handle_CheckAndSignalAlarms(HLERequestContext& ctx);
void Handle_GetClosestAlarmInfo(HLERequestContext& ctx);
Core::System& m_system;
std::shared_ptr<TimeManager> m_time;
ServerManager& m_server_manager;
bool m_is_s_and_p_setup{};
StandardLocalSystemClockCore& m_local_system_clock;
StandardUserSystemClockCore& m_user_system_clock;
StandardNetworkSystemClockCore& m_network_system_clock;
StandardSteadyClockCore& m_steady_clock;
TimeZone& m_time_zone;
EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
SharedMemory& m_shared_memory;
Alarms& m_alarms;
LocalSystemClockContextWriter& m_local_system_context_writer;
NetworkSystemClockContextWriter& m_network_system_context_writer;
EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer;
OperationEvent m_local_operation;
OperationEvent m_network_operation;
OperationEvent m_ephemeral_operation;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/psc/time/shared_memory.h"
namespace Service::PSC::Time {
namespace {
template <typename T>
constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
while (true) {
// Get the counter.
auto counter = p->m_counter;
// Get the value.
auto value = p->m_value[counter % 2];
// Fence memory.
std::atomic_thread_fence(std::memory_order_acquire);
// Check that the counter matches.
if (counter == p->m_counter) {
return value;
}
}
}
template <typename T>
constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
// Get the current counter.
auto counter = p->m_counter;
// Increment the counter.
++counter;
// Store the updated value.
p->m_value[counter % 2] = value;
// Fence memory.
std::atomic_thread_fence(std::memory_order_release);
// Set the updated counter.
p->m_counter = counter;
}
} // namespace
SharedMemory::SharedMemory(Core::System& system)
: m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()},
m_shared_memory_ptr{reinterpret_cast<SharedMemoryStruct*>(m_k_shared_memory.GetPointer())} {
std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr));
}
void SharedMemory::SetLocalSystemContext(SystemClockContext& context) {
WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context);
}
void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) {
WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context);
}
void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_point) {
WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points,
{time_point, clock_source_id});
}
void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) {
WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point);
}
void SharedMemory::SetAutomaticCorrection(bool automatic_correction) {
WriteToLockFreeAtomicType(&m_shared_memory_ptr->automatic_corrections, automatic_correction);
}
void SharedMemory::UpdateBaseTime(s64 time) {
SteadyClockTimePoint time_point{
ReadFromLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points)};
time_point.time_point = time;
WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, time_point);
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/service/psc/time/common.h"
namespace Core {
class System;
}
namespace Kernel {
class KSharedMemory;
}
namespace Service::PSC::Time {
template <typename T>
struct LockFreeAtomicType {
u32 m_counter;
std::array<T, 2> m_value;
};
struct SharedMemoryStruct {
LockFreeAtomicType<SteadyClockTimePoint> steady_time_points;
LockFreeAtomicType<SystemClockContext> local_system_clock_contexts;
LockFreeAtomicType<SystemClockContext> network_system_clock_contexts;
LockFreeAtomicType<bool> automatic_corrections;
LockFreeAtomicType<ContinuousAdjustmentTimePoint> continuous_adjustment_time_points;
std::array<char, 0xEB8> pad0148;
};
static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0,
"steady_time_points are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38,
"local_system_clock_contexts are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80,
"network_system_clock_contexts are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8,
"automatic_corrections are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0,
"continuous_adjustment_time_points are in the wrong place!");
static_assert(sizeof(SharedMemoryStruct) == 0x1000,
"Time's SharedMemoryStruct has the wrong size!");
static_assert(std::is_trivial_v<SharedMemoryStruct>);
class SharedMemory {
public:
explicit SharedMemory(Core::System& system);
Kernel::KSharedMemory& GetKSharedMemory() {
return m_k_shared_memory;
}
void SetLocalSystemContext(SystemClockContext& context);
void SetNetworkSystemContext(SystemClockContext& context);
void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff);
void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point);
void SetAutomaticCorrection(bool automatic_correction);
void UpdateBaseTime(s64 time);
private:
Core::System& m_system;
Kernel::KSharedMemory& m_k_shared_memory;
SharedMemoryStruct* m_shared_memory_ptr;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,500 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/psc/time/shared_memory.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/psc/time/steady_clock.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone.h"
#include "core/hle/service/psc/time/time_zone_service.h"
namespace Service::PSC::Time {
namespace {
constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePoint& time_point,
SystemClockContext& context) {
R_UNLESS(out_time != nullptr, ResultInvalidArgument);
R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);
*out_time = context.offset + time_point.time_point;
R_SUCCEED();
}
} // namespace
StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info,
std::shared_ptr<TimeManager> time, const char* name)
: ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time},
m_local_system_clock{m_time->m_standard_local_system_clock},
m_user_system_clock{m_time->m_standard_user_system_clock},
m_network_system_clock{m_time->m_standard_network_system_clock},
m_time_zone{m_time->m_time_zone},
m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{
m_time->m_shared_memory} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
{1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
{2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
{3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
{4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
{5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
{20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
{50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
{51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
{100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
{101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
{102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
{200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
{201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
{300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
{400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
{401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
{500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
{501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
};
// clang-format on
RegisterHandlers(functions);
}
Result StaticService::GetClockSnapshotImpl(ClockSnapshot& out_snapshot,
SystemClockContext& user_context,
SystemClockContext& network_context, TimeType type) {
out_snapshot.user_context = user_context;
out_snapshot.network_context = network_context;
R_TRY(
m_time->m_standard_steady_clock.GetCurrentTimePoint(out_snapshot.steady_clock_time_point));
out_snapshot.is_automatic_correction_enabled = m_user_system_clock.GetAutomaticCorrection();
R_TRY(m_time_zone.GetLocationName(out_snapshot.location_name));
R_TRY(GetTimeFromTimePointAndContext(
&out_snapshot.user_time, out_snapshot.steady_clock_time_point, out_snapshot.user_context));
R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.user_calendar_time,
out_snapshot.user_calendar_additional_time,
out_snapshot.user_time));
if (GetTimeFromTimePointAndContext(&out_snapshot.network_time,
out_snapshot.steady_clock_time_point,
out_snapshot.network_context) != ResultSuccess) {
out_snapshot.network_time = 0;
}
R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.network_calendar_time,
out_snapshot.network_calendar_additional_time,
out_snapshot.network_time));
out_snapshot.type = type;
out_snapshot.unk_CE = 0;
R_SUCCEED();
}
void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<SystemClock> service{};
auto res = GetStandardUserSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<SystemClock>(std::move(service));
}
void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<SystemClock> service{};
auto res = GetStandardNetworkSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<SystemClock>(std::move(service));
}
void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<SteadyClock> service{};
auto res = GetStandardSteadyClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(service));
}
void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<TimeZoneService> service{};
auto res = GetTimeZoneService(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(service));
}
void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<SystemClock> service{};
auto res = GetStandardLocalSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<SystemClock>(std::move(service));
}
void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
std::shared_ptr<SystemClock> service{};
auto res = GetEphemeralNetworkSystemClock(service);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface<SystemClock>(std::move(service));
}
void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KSharedMemory* shared_memory{};
auto res = GetSharedMemoryNativeHandle(&shared_memory);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(shared_memory);
}
void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(m_setup_info.can_write_steady_clock ? ResultNotImplemented : ResultPermissionDenied);
}
void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNotImplemented);
}
void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
bool is_enabled{};
auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push<bool>(is_enabled);
}
void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto automatic_correction{rp.Pop<bool>()};
auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNotImplemented);
}
void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
bool is_sufficient{};
auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push<bool>(is_sufficient);
}
void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
SteadyClockTimePoint time_point{};
auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<SteadyClockTimePoint>(time_point);
}
void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto context{rp.PopRaw<SystemClockContext>()};
s64 time{};
auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push<s64>(time);
}
void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto type{rp.PopEnum<TimeType>()};
ClockSnapshot snapshot{};
auto res = GetClockSnapshot(snapshot, type);
ctx.WriteBuffer(snapshot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto clock_type{rp.PopEnum<TimeType>()};
[[maybe_unused]] auto alignment{rp.Pop<u32>()};
auto user_context{rp.PopRaw<SystemClockContext>()};
auto network_context{rp.PopRaw<SystemClockContext>()};
ClockSnapshot snapshot{};
auto res =
GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
ctx.WriteBuffer(snapshot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
ClockSnapshot a{};
ClockSnapshot b{};
auto a_buffer{ctx.ReadBuffer(0)};
auto b_buffer{ctx.ReadBuffer(1)};
std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot));
std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot));
s64 difference{};
auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(difference);
}
void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
ClockSnapshot a{};
ClockSnapshot b{};
auto a_buffer{ctx.ReadBuffer(0)};
auto b_buffer{ctx.ReadBuffer(1)};
std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot));
std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot));
s64 time{};
auto res = CalculateSpanBetween(time, a, b);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(time);
}
// =============================== Implementations ===========================
Result StaticService::GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service) {
out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock,
m_setup_info.can_write_user_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
}
Result StaticService::GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) {
out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock,
m_setup_info.can_write_network_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
}
Result StaticService::GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service) {
out_service =
std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
}
Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
out_service =
std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone,
m_setup_info.can_write_timezone_device_location);
R_SUCCEED();
}
Result StaticService::GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service) {
out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock,
m_setup_info.can_write_local_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
}
Result StaticService::GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) {
out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock,
m_setup_info.can_write_network_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
}
Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
*out_shared_memory = &m_shared_memory.GetKSharedMemory();
R_SUCCEED();
}
Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled) {
R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
out_is_enabled = m_user_system_clock.GetAutomaticCorrection();
R_SUCCEED();
}
Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
bool automatic_correction) {
R_UNLESS(m_user_system_clock.IsInitialized() && m_time->m_standard_steady_clock.IsInitialized(),
ResultClockUninitialized);
R_UNLESS(m_setup_info.can_write_user_clock, ResultPermissionDenied);
R_TRY(m_user_system_clock.SetAutomaticCorrection(automatic_correction));
m_shared_memory.SetAutomaticCorrection(automatic_correction);
SteadyClockTimePoint time_point{};
R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));
m_user_system_clock.SetTimePointAndSignal(time_point);
m_user_system_clock.GetEvent().Signal();
R_SUCCEED();
}
Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
out_is_sufficient = m_network_system_clock.IsAccuracySufficient();
R_SUCCEED();
}
Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
SteadyClockTimePoint& out_time_point) {
R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
m_user_system_clock.GetTimePoint(out_time_point);
R_SUCCEED();
}
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(s64& out_time,
SystemClockContext& context) {
R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized);
SteadyClockTimePoint time_point{};
R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));
R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);
auto one_second_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
auto ticks{m_system.CoreTiming().GetClockTicks()};
auto current_time{ConvertToTimeSpan(ticks).count()};
out_time = ((context.offset + time_point.time_point) - (current_time / one_second_ns));
R_SUCCEED();
}
Result StaticService::GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type) {
SystemClockContext user_context{};
R_TRY(m_user_system_clock.GetContext(user_context));
SystemClockContext network_context{};
R_TRY(m_network_system_clock.GetContext(network_context));
R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
}
Result StaticService::GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot,
SystemClockContext& user_context,
SystemClockContext& network_context,
TimeType type) {
R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
}
Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
ClockSnapshot& a,
ClockSnapshot& b) {
auto diff_s =
std::chrono::seconds(b.user_context.offset) - std::chrono::seconds(a.user_context.offset);
if (a.user_context == b.user_context ||
!a.user_context.steady_time_point.IdMatches(b.user_context.steady_time_point)) {
out_time = 0;
R_SUCCEED();
}
if (!a.is_automatic_correction_enabled || !b.is_automatic_correction_enabled) {
out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
R_SUCCEED();
}
if (a.network_context.steady_time_point.IdMatches(a.steady_clock_time_point) ||
b.network_context.steady_time_point.IdMatches(b.steady_clock_time_point)) {
out_time = 0;
R_SUCCEED();
}
out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
R_SUCCEED();
}
Result StaticService::CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b) {
s64 time_s{};
auto res =
GetSpanBetweenTimePoints(&time_s, a.steady_clock_time_point, b.steady_clock_time_point);
if (res != ResultSuccess) {
R_UNLESS(a.network_time != 0 && b.network_time != 0, ResultTimeNotFound);
time_s = b.network_time - a.network_time;
}
out_time =
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(time_s)).count();
R_SUCCEED();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KSharedMemory;
}
namespace Service::PSC::Time {
class TimeManager;
class StandardLocalSystemClockCore;
class StandardUserSystemClockCore;
class StandardNetworkSystemClockCore;
class TimeZone;
class SystemClock;
class SteadyClock;
class TimeZoneService;
class EphemeralNetworkSystemClockCore;
class SharedMemory;
class StaticService final : public ServiceFramework<StaticService> {
public:
explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info,
std::shared_ptr<TimeManager> time, const char* name);
~StaticService() override = default;
Result GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service);
Result GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service);
Result GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service);
Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
Result GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service);
Result GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service);
Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled);
Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
SteadyClockTimePoint& out_time_point);
Result CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, SystemClockContext& context);
Result GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type);
Result GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot,
SystemClockContext& user_context,
SystemClockContext& network_context,
TimeType type);
Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, ClockSnapshot& a,
ClockSnapshot& b);
Result CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b);
private:
Result GetClockSnapshotImpl(ClockSnapshot& out_snapshot, SystemClockContext& user_context,
SystemClockContext& network_context, TimeType type);
void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
void Handle_GetTimeZoneService(HLERequestContext& ctx);
void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
void Handle_GetClockSnapshot(HLERequestContext& ctx);
void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
void Handle_CalculateSpanBetween(HLERequestContext& ctx);
Core::System& m_system;
StaticServiceSetupInfo m_setup_info;
std::shared_ptr<TimeManager> m_time;
StandardLocalSystemClockCore& m_local_system_clock;
StandardUserSystemClockCore& m_user_system_clock;
StandardNetworkSystemClockCore& m_network_system_clock;
TimeZone& m_time_zone;
EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
SharedMemory& m_shared_memory;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,164 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/psc/time/steady_clock.h"
namespace Service::PSC::Time {
SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> manager,
bool can_write_steady_clock, bool can_write_uninitialized_clock)
: ServiceFramework{system_, "ISteadyClock"}, m_system{system},
m_clock_core{manager->m_standard_steady_clock},
m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{
can_write_uninitialized_clock} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &SteadyClock::Handle_GetCurrentTimePoint, "GetCurrentTimePoint"},
{2, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"},
{3, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"},
{100, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"},
{101, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"},
{102, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"},
{200, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"},
};
// clang-format on
RegisterHandlers(functions);
}
void SteadyClock::Handle_GetCurrentTimePoint(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
SteadyClockTimePoint time_point{};
auto res = GetCurrentTimePoint(time_point);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<SteadyClockTimePoint>(time_point);
}
void SteadyClock::Handle_GetTestOffset(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
s64 test_offset{};
auto res = GetTestOffset(test_offset);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(test_offset);
}
void SteadyClock::Handle_SetTestOffset(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto test_offset{rp.Pop<s64>()};
auto res = SetTestOffset(test_offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void SteadyClock::Handle_GetRtcValue(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
s64 rtc_value{};
auto res = GetRtcValue(rtc_value);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(rtc_value);
}
void SteadyClock::Handle_IsRtcResetDetected(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
bool reset_detected{false};
auto res = IsRtcResetDetected(reset_detected);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(reset_detected);
}
void SteadyClock::Handle_GetSetupResultValue(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Result result_value{ResultSuccess};
auto res = GetSetupResultValue(result_value);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(result_value);
}
void SteadyClock::Handle_GetInternalOffset(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
s64 internal_offset{};
auto res = GetInternalOffset(internal_offset);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(internal_offset);
}
// =============================== Implementations ===========================
Result SteadyClock::GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
R_RETURN(m_clock_core.GetCurrentTimePoint(out_time_point));
}
Result SteadyClock::GetTestOffset(s64& out_test_offset) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
out_test_offset = m_clock_core.GetTestOffset();
R_SUCCEED();
}
Result SteadyClock::SetTestOffset(s64 test_offset) {
R_UNLESS(m_can_write_steady_clock, ResultPermissionDenied);
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
m_clock_core.SetTestOffset(test_offset);
R_SUCCEED();
}
Result SteadyClock::GetRtcValue(s64& out_rtc_value) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
R_RETURN(m_clock_core.GetRtcValue(out_rtc_value));
}
Result SteadyClock::IsRtcResetDetected(bool& out_is_detected) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
out_is_detected = m_clock_core.IsResetDetected();
R_SUCCEED();
}
Result SteadyClock::GetSetupResultValue(Result& out_result) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
out_result = m_clock_core.GetSetupResultValue();
R_SUCCEED();
}
Result SteadyClock::GetInternalOffset(s64& out_internal_offset) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
out_internal_offset = m_clock_core.GetInternalOffset();
R_SUCCEED();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class SteadyClock final : public ServiceFramework<SteadyClock> {
public:
explicit SteadyClock(Core::System& system, std::shared_ptr<TimeManager> manager,
bool can_write_steady_clock, bool can_write_uninitialized_clock);
~SteadyClock() override = default;
Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point);
Result GetTestOffset(s64& out_test_offset);
Result SetTestOffset(s64 test_offset);
Result GetRtcValue(s64& out_rtc_value);
Result IsRtcResetDetected(bool& out_is_detected);
Result GetSetupResultValue(Result& out_result);
Result GetInternalOffset(s64& out_internal_offset);
private:
void Handle_GetCurrentTimePoint(HLERequestContext& ctx);
void Handle_GetTestOffset(HLERequestContext& ctx);
void Handle_SetTestOffset(HLERequestContext& ctx);
void Handle_GetRtcValue(HLERequestContext& ctx);
void Handle_IsRtcResetDetected(HLERequestContext& ctx);
void Handle_GetSetupResultValue(HLERequestContext& ctx);
void Handle_GetInternalOffset(HLERequestContext& ctx);
Core::System& m_system;
StandardSteadyClockCore& m_clock_core;
bool m_can_write_steady_clock;
bool m_can_write_uninitialized_clock;
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/psc/time/system_clock.h"
namespace Service::PSC::Time {
SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock,
bool can_write_uninitialized_clock)
: ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core},
m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{
can_write_uninitialized_clock} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &SystemClock::Handle_GetCurrentTime, "GetCurrentTime"},
{1, &SystemClock::Handle_SetCurrentTime, "SetCurrentTime"},
{2, &SystemClock::Handle_GetSystemClockContext, "GetSystemClockContext"},
{3, &SystemClock::Handle_SetSystemClockContext, "SetSystemClockContext"},
{4, &SystemClock::Handle_GetOperationEventReadableHandle, "GetOperationEventReadableHandle"},
};
// clang-format on
RegisterHandlers(functions);
}
void SystemClock::Handle_GetCurrentTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
s64 time{};
auto res = GetCurrentTime(time);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push<s64>(time);
}
void SystemClock::Handle_SetCurrentTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto time{rp.Pop<s64>()};
auto res = SetCurrentTime(time);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void SystemClock::Handle_GetSystemClockContext(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
SystemClockContext context{};
auto res = GetSystemClockContext(context);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(SystemClockContext) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<SystemClockContext>(context);
}
void SystemClock::Handle_SetSystemClockContext(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto context{rp.PopRaw<SystemClockContext>()};
auto res = SetSystemClockContext(context);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void SystemClock::Handle_GetOperationEventReadableHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
Kernel::KEvent* event{};
auto res = GetOperationEventReadableHandle(&event);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(res);
rb.PushCopyObjects(event->GetReadableEvent());
}
// =============================== Implementations ===========================
Result SystemClock::GetCurrentTime(s64& out_time) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
R_RETURN(m_clock_core.GetCurrentTime(&out_time));
}
Result SystemClock::SetCurrentTime(s64 time) {
R_UNLESS(m_can_write_clock, ResultPermissionDenied);
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
R_RETURN(m_clock_core.SetCurrentTime(time));
}
Result SystemClock::GetSystemClockContext(SystemClockContext& out_context) {
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
R_RETURN(m_clock_core.GetContext(out_context));
}
Result SystemClock::SetSystemClockContext(SystemClockContext& context) {
R_UNLESS(m_can_write_clock, ResultPermissionDenied);
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
ResultClockUninitialized);
R_RETURN(m_clock_core.SetContextAndWrite(context));
}
Result SystemClock::GetOperationEventReadableHandle(Kernel::KEvent** out_event) {
if (!m_operation_event) {
m_operation_event = std::make_unique<OperationEvent>(m_system);
R_UNLESS(m_operation_event != nullptr, ResultFailed);
m_clock_core.LinkOperationEvent(*m_operation_event);
}
*out_event = m_operation_event->m_event;
R_SUCCEED();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::PSC::Time {
class SystemClock final : public ServiceFramework<SystemClock> {
public:
explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core,
bool can_write_clock, bool can_write_uninitialized_clock);
~SystemClock() override = default;
Result GetCurrentTime(s64& out_time);
Result SetCurrentTime(s64 time);
Result GetSystemClockContext(SystemClockContext& out_context);
Result SetSystemClockContext(SystemClockContext& context);
Result GetOperationEventReadableHandle(Kernel::KEvent** out_event);
private:
void Handle_GetCurrentTime(HLERequestContext& ctx);
void Handle_SetCurrentTime(HLERequestContext& ctx);
void Handle_GetSystemClockContext(HLERequestContext& ctx);
void Handle_SetSystemClockContext(HLERequestContext& ctx);
void Handle_GetOperationEventReadableHandle(HLERequestContext& ctx);
Core::System& m_system;
SystemClockCore& m_clock_core;
bool m_can_write_clock;
bool m_can_write_uninitialized_clock;
std::unique_ptr<OperationEvent> m_operation_event{};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,280 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/psc/time/time_zone.h"
namespace Service::PSC::Time {
namespace {
constexpr Result ValidateRule(Tz::Rule& rule) {
if (rule.typecnt > static_cast<s32>(Tz::TZ_MAX_TYPES) ||
rule.timecnt > static_cast<s32>(Tz::TZ_MAX_TIMES) ||
rule.charcnt > static_cast<s32>(Tz::TZ_MAX_CHARS)) {
R_RETURN(ResultTimeZoneOutOfRange);
}
for (s32 i = 0; i < rule.timecnt; i++) {
if (rule.types[i] >= rule.typecnt) {
R_RETURN(ResultTimeZoneOutOfRange);
}
}
for (s32 i = 0; i < rule.typecnt; i++) {
if (rule.ttis[i].tt_desigidx >= static_cast<s32>(rule.chars.size())) {
R_RETURN(ResultTimeZoneOutOfRange);
}
}
R_SUCCEED();
}
constexpr bool GetTimeZoneTime(s64& out_time, Tz::Rule& rule, s64 time, s32 index,
s32 index_offset) {
s32 found_idx{};
s32 expected_index{index + index_offset};
s64 time_to_find{time + rule.ttis[rule.types[index]].tt_utoff -
rule.ttis[rule.types[expected_index]].tt_utoff};
if (rule.timecnt > 1 && rule.ats[0] <= time_to_find) {
s32 low{1};
s32 high{rule.timecnt};
while (low < high) {
auto mid{(low + high) / 2};
if (rule.ats[mid] <= time_to_find) {
low = mid + 1;
} else if (rule.ats[mid] > time_to_find) {
high = mid;
}
}
found_idx = low - 1;
}
if (found_idx == expected_index) {
out_time = time_to_find;
}
return found_idx == expected_index;
}
} // namespace
void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) {
std::scoped_lock l{m_mutex};
m_steady_clock_time_point = time_point;
}
void TimeZone::SetTotalLocationNameCount(u32 count) {
std::scoped_lock l{m_mutex};
m_total_location_name_count = count;
}
void TimeZone::SetRuleVersion(RuleVersion& rule_version) {
std::scoped_lock l{m_mutex};
m_rule_version = rule_version;
}
Result TimeZone::GetLocationName(LocationName& out_name) {
std::scoped_lock l{m_mutex};
R_UNLESS(m_initialized, ResultClockUninitialized);
out_name = m_location;
R_SUCCEED();
}
Result TimeZone::GetTotalLocationCount(u32& out_count) {
std::scoped_lock l{m_mutex};
if (!m_initialized) {
return ResultClockUninitialized;
}
out_count = m_total_location_name_count;
R_SUCCEED();
}
Result TimeZone::GetRuleVersion(RuleVersion& out_rule_version) {
std::scoped_lock l{m_mutex};
if (!m_initialized) {
return ResultClockUninitialized;
}
out_rule_version = m_rule_version;
R_SUCCEED();
}
Result TimeZone::GetTimePoint(SteadyClockTimePoint& out_time_point) {
std::scoped_lock l{m_mutex};
if (!m_initialized) {
return ResultClockUninitialized;
}
out_time_point = m_steady_clock_time_point;
R_SUCCEED();
}
Result TimeZone::ToCalendarTime(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time,
Tz::Rule& rule) {
std::scoped_lock l{m_mutex};
R_RETURN(ToCalendarTimeImpl(out_calendar_time, out_additional_info, time, rule));
}
Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
CalendarAdditionalInfo& calendar_additional, s64 time) {
// This is checked outside the mutex. Bug?
if (!m_initialized) {
return ResultClockUninitialized;
}
std::scoped_lock l{m_mutex};
R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule));
}
Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) {
std::scoped_lock l{m_mutex};
Tz::Rule tmp_rule{};
R_TRY(ParseBinaryImpl(tmp_rule, binary));
m_my_rule = tmp_rule;
m_location = name;
R_SUCCEED();
}
Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary) {
std::scoped_lock l{m_mutex};
R_RETURN(ParseBinaryImpl(out_rule, binary));
}
Result TimeZone::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
CalendarTime& calendar, Tz::Rule& rule) {
std::scoped_lock l{m_mutex};
auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1);
if (res != ResultSuccess) {
if (res == ResultTimeZoneNotFound) {
res = ResultSuccess;
out_count = 0;
}
} else if (out_count == 2 && out_times[0] > out_times[1]) {
std::swap(out_times[0], out_times[1]);
}
R_RETURN(res);
}
Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
u32 out_times_count, CalendarTime& calendar) {
std::scoped_lock l{m_mutex};
auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1);
if (res != ResultSuccess) {
if (res == ResultTimeZoneNotFound) {
res = ResultSuccess;
out_count = 0;
}
} else if (out_count == 2 && out_times[0] > out_times[1]) {
std::swap(out_times[0], out_times[1]);
}
R_RETURN(res);
}
Result TimeZone::ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary) {
if (Tz::ParseTimeZoneBinary(out_rule, binary)) {
R_RETURN(ResultTimeZoneParseFailed);
}
R_SUCCEED();
}
Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time,
Tz::Rule& rule) {
R_TRY(ValidateRule(rule));
Tz::CalendarTimeInternal calendar_internal{};
time_t time_tmp{static_cast<time_t>(time)};
if (Tz::localtime_rz(&calendar_internal, &rule, &time_tmp)) {
R_RETURN(ResultOverflow);
}
out_calendar_time.year = static_cast<s16>(calendar_internal.tm_year + 1900);
out_calendar_time.month = static_cast<s8>(calendar_internal.tm_mon + 1);
out_calendar_time.day = static_cast<s8>(calendar_internal.tm_mday);
out_calendar_time.hour = static_cast<s8>(calendar_internal.tm_hour);
out_calendar_time.minute = static_cast<s8>(calendar_internal.tm_min);
out_calendar_time.second = static_cast<s8>(calendar_internal.tm_sec);
out_additional_info.day_of_week = calendar_internal.tm_wday;
out_additional_info.day_of_year = calendar_internal.tm_yday;
std::memcpy(out_additional_info.name.data(), calendar_internal.tm_zone.data(),
out_additional_info.name.size());
out_additional_info.name[out_additional_info.name.size() - 1] = '\0';
out_additional_info.is_dst = calendar_internal.tm_isdst;
out_additional_info.ut_offset = calendar_internal.tm_utoff;
R_SUCCEED();
}
Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
CalendarTime& calendar, Tz::Rule& rule, s32 is_dst) {
R_TRY(ValidateRule(rule));
calendar.month -= 1;
calendar.year -= 1900;
Tz::CalendarTimeInternal internal{
.tm_sec = calendar.second,
.tm_min = calendar.minute,
.tm_hour = calendar.hour,
.tm_mday = calendar.day,
.tm_mon = calendar.month,
.tm_year = calendar.year,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = is_dst,
.tm_zone = {},
.tm_utoff = 0,
.time_index = 0,
};
time_t time_tmp{};
auto res = Tz::mktime_tzname(&time_tmp, &rule, &internal);
s64 time = static_cast<s64>(time_tmp);
if (res == 1) {
R_RETURN(ResultOverflow);
} else if (res == 2) {
R_RETURN(ResultTimeZoneNotFound);
}
if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute ||
internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day ||
internal.tm_mon != calendar.month || internal.tm_year != calendar.year) {
R_RETURN(ResultTimeZoneNotFound);
}
if (res != 0) {
ASSERT(false);
}
out_times[0] = time;
if (out_times_count < 2) {
out_count = 1;
R_SUCCEED();
}
s64 time2{};
if (internal.time_index > 0 && GetTimeZoneTime(time2, rule, time, internal.time_index, -1)) {
out_times[1] = time2;
out_count = 2;
R_SUCCEED();
}
if (((internal.time_index + 1) < rule.timecnt) &&
GetTimeZoneTime(time2, rule, time, internal.time_index, 1)) {
out_times[1] = time2;
out_count = 2;
R_SUCCEED();
}
out_count = 1;
R_SUCCEED();
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <span>
#include <tz/tz.h>
#include "core/hle/service/psc/time/common.h"
namespace Service::PSC::Time {
class TimeZone {
public:
TimeZone() = default;
bool IsInitialized() const {
return m_initialized;
}
void SetInitialized() {
m_initialized = true;
}
void SetTimePoint(SteadyClockTimePoint& time_point);
void SetTotalLocationNameCount(u32 count);
void SetRuleVersion(RuleVersion& rule_version);
Result GetLocationName(LocationName& out_name);
Result GetTotalLocationCount(u32& out_count);
Result GetRuleVersion(RuleVersion& out_rule_version);
Result GetTimePoint(SteadyClockTimePoint& out_time_point);
Result ToCalendarTime(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule);
Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
CalendarAdditionalInfo& calendar_additional, s64 time);
Result ParseBinary(LocationName& name, std::span<const u8> binary);
Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary);
Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
CalendarTime& calendar, Tz::Rule& rule);
Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
CalendarTime& calendar);
private:
Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary);
Result ToCalendarTimeImpl(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time,
Tz::Rule& rule);
Result ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
CalendarTime& calendar, Tz::Rule& rule, s32 is_dst);
bool m_initialized{};
std::recursive_mutex m_mutex;
LocationName m_location{};
Tz::Rule m_my_rule{};
SteadyClockTimePoint m_steady_clock_time_point{};
u32 m_total_location_name_count{};
RuleVersion m_rule_version{};
};
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,289 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <tz/tz.h>
#include "core/core.h"
#include "core/hle/service/psc/time/time_zone_service.h"
namespace Service::PSC::Time {
TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core,
TimeZone& time_zone, bool can_write_timezone_device_location)
: ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core},
m_time_zone{time_zone}, m_can_write_timezone_device_location{
can_write_timezone_device_location} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
{1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
{2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
{3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
{4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
{5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
{6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
{7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
{8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
{20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
{100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
{101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
{201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
{202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
};
// clang-format on
RegisterHandlers(functions);
}
void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
LocationName name{};
auto res = GetDeviceLocationName(name);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(LocationName) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<LocationName>(name);
}
void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
[[maybe_unused]] auto name{rp.PopRaw<LocationName>()};
if (!m_can_write_timezone_device_location) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultPermissionDenied);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNotImplemented);
}
void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
u32 count{};
auto res = GetTotalLocationNameCount(count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(count);
}
void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNotImplemented);
}
void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNotImplemented);
}
void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
RuleVersion rule_version{};
auto res = GetTimeZoneRuleVersion(rule_version);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(RuleVersion) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<RuleVersion>(rule_version);
}
void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
LocationName name{};
SteadyClockTimePoint time_point{};
auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(LocationName) / sizeof(u32)) +
(sizeof(SteadyClockTimePoint) / sizeof(u32))};
rb.Push(res);
rb.PushRaw<LocationName>(name);
rb.PushRaw<SteadyClockTimePoint>(time_point);
}
void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto name{rp.PopRaw<LocationName>()};
auto binary{ctx.ReadBuffer()};
auto res = SetDeviceLocationNameWithTimeZoneRule(name, binary);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
auto binary{ctx.ReadBuffer()};
Tz::Rule rule{};
auto res = ParseTimeZoneBinary(rule, binary);
ctx.WriteBuffer(rule);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNotImplemented);
}
void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto time{rp.Pop<s64>()};
auto rule_buffer{ctx.ReadBuffer()};
Tz::Rule rule{};
std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
CalendarTime calendar_time{};
CalendarAdditionalInfo additional_info{};
auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) +
(sizeof(CalendarAdditionalInfo) / sizeof(u32))};
rb.Push(res);
rb.PushRaw<CalendarTime>(calendar_time);
rb.PushRaw<CalendarAdditionalInfo>(additional_info);
}
void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto time{rp.Pop<s64>()};
CalendarTime calendar_time{};
CalendarAdditionalInfo additional_info{};
auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) +
(sizeof(CalendarAdditionalInfo) / sizeof(u32))};
rb.Push(res);
rb.PushRaw<CalendarTime>(calendar_time);
rb.PushRaw<CalendarAdditionalInfo>(additional_info);
}
void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto calendar{rp.PopRaw<CalendarTime>()};
auto binary{ctx.ReadBuffer()};
Tz::Rule rule{};
std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
u32 count{};
std::array<s64, 2> times{};
u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
auto res = ToPosixTime(count, times, times_count, calendar, rule);
ctx.WriteBuffer(times);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(count);
}
void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto calendar{rp.PopRaw<CalendarTime>()};
u32 count{};
std::array<s64, 2> times{};
u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
ctx.WriteBuffer(times);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(count);
}
// =============================== Implementations ===========================
Result TimeZoneService::GetDeviceLocationName(LocationName& out_location_name) {
R_RETURN(m_time_zone.GetLocationName(out_location_name));
}
Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
R_RETURN(m_time_zone.GetTotalLocationCount(out_count));
}
Result TimeZoneService::GetTimeZoneRuleVersion(RuleVersion& out_rule_version) {
R_RETURN(m_time_zone.GetRuleVersion(out_rule_version));
}
Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point,
LocationName& location_name) {
R_TRY(m_time_zone.GetLocationName(location_name));
R_RETURN(m_time_zone.GetTimePoint(out_time_point));
}
Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name,
std::span<const u8> binary) {
R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied);
R_TRY(m_time_zone.ParseBinary(location_name, binary));
SteadyClockTimePoint time_point{};
R_TRY(m_clock_core.GetCurrentTimePoint(time_point));
m_time_zone.SetTimePoint(time_point);
R_SUCCEED();
}
Result TimeZoneService::ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary) {
R_RETURN(m_time_zone.ParseBinaryInto(out_rule, binary));
}
Result TimeZoneService::ToCalendarTime(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time,
Tz::Rule& rule) {
R_RETURN(m_time_zone.ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
}
Result TimeZoneService::ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info,
s64 time) {
R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
}
Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
u32 out_times_count, CalendarTime& calendar_time,
Tz::Rule& rule) {
R_RETURN(m_time_zone.ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
}
Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
u32 out_times_count, CalendarTime& calendar_time) {
R_RETURN(
m_time_zone.ToPosixTimeWithMyRule(out_count, out_times, out_times_count, calendar_time));
}
} // namespace Service::PSC::Time

View File

@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Tz {
struct Rule;
}
namespace Service::PSC::Time {
class TimeZoneService final : public ServiceFramework<TimeZoneService> {
public:
explicit TimeZoneService(Core::System& system, StandardSteadyClockCore& clock_core,
TimeZone& time_zone, bool can_write_timezone_device_location);
~TimeZoneService() override = default;
Result GetDeviceLocationName(LocationName& out_location_name);
Result GetTotalLocationNameCount(u32& out_count);
Result GetTimeZoneRuleVersion(RuleVersion& out_rule_version);
Result GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point,
LocationName& location_name);
Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name,
std::span<const u8> binary);
Result ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary);
Result ToCalendarTime(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule);
Result ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time,
CalendarAdditionalInfo& out_additional_info, s64 time);
Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
CalendarTime& calendar_time, Tz::Rule& rule);
Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
CalendarTime& calendar_time);
private:
void Handle_GetDeviceLocationName(HLERequestContext& ctx);
void Handle_SetDeviceLocationName(HLERequestContext& ctx);
void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
void Handle_LoadLocationNameList(HLERequestContext& ctx);
void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
void Handle_ToCalendarTime(HLERequestContext& ctx);
void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
void Handle_ToPosixTime(HLERequestContext& ctx);
void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
Core::System& m_system;
StandardSteadyClockCore& m_clock_core;
TimeZone& m_time_zone;
bool m_can_write_timezone_device_location;
};
} // namespace Service::PSC::Time

View File

@@ -66,7 +66,6 @@
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/spl/spl_module.h"
#include "core/hle/service/ssl/ssl.h"
#include "core/hle/service/time/time.h"
#include "core/hle/service/usb/usb.h"
#include "core/hle/service/vi/vi.h"
#include "core/reporter.h"
@@ -246,6 +245,9 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
// glue depends on settings and psc, so they must come first
kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
@@ -269,13 +271,10 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("time", [&] { Time::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
// clang-format on
}

View File

@@ -9,7 +9,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/uuid.h"
#include "core/hle/service/time/clock_types.h"
#include "core/hle/service/psc/time/common.h"
namespace Service::Set {
@@ -29,14 +29,14 @@ static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid s
struct InitialLaunchSettings {
InitialLaunchFlag flags;
INSERT_PADDING_BYTES(0x4);
Service::Time::Clock::SteadyClockTimePoint timestamp;
Service::PSC::Time::SteadyClockTimePoint timestamp;
};
static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
#pragma pack(push, 4)
struct InitialLaunchSettingsPacked {
InitialLaunchFlag flags;
Service::Time::Clock::SteadyClockTimePoint timestamp;
Service::PSC::Time::SteadyClockTimePoint timestamp;
};
#pragma pack(pop)
static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,

View File

@@ -1,12 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/set/appln_settings.h"
#include "core/hle/service/set/setting_formats/appln_settings.h"
namespace Service::Set {
ApplnSettings DefaultApplnSettings() {
return {};
ApplnSettings settings{};
settings.mii_author_id = Common::UUID::MakeDefault();
return settings;
}
} // namespace Service::Set

View File

@@ -7,24 +7,23 @@
#include <cstddef>
#include "common/common_types.h"
#include "common/uuid.h"
#include "core/hle/service/set/settings_types.h"
namespace Service::Set {
struct ApplnSettings {
std::array<u8, 0x10> reserved_000;
INSERT_PADDING_BYTES(0x10); // Reserved
// nn::util::Uuid MiiAuthorId, copied from system settings 0x94B0
std::array<u8, 0x10> mii_author_id;
std::array<u8, 0x30> reserved_020;
Common::UUID mii_author_id;
INSERT_PADDING_BYTES(0x30); // Reserved
// nn::settings::system::ServiceDiscoveryControlSettings
std::array<u8, 0x4> service_discovery_control_settings;
std::array<u8, 0x20> reserved_054;
u32 service_discovery_control_settings;
INSERT_PADDING_BYTES(0x20); // Reserved
bool in_repair_process_enable_flag;
std::array<u8, 0x3> pad_075;
INSERT_PADDING_BYTES(0x3);
};
static_assert(offsetof(ApplnSettings, mii_author_id) == 0x10);
static_assert(offsetof(ApplnSettings, service_discovery_control_settings) == 0x50);

View File

@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/set/device_settings.h"
#include "core/hle/service/set/setting_formats/device_settings.h"
namespace Service::Set {

View File

@@ -7,10 +7,12 @@
#include <cstddef>
#include "common/common_types.h"
#include "common/vector_math.h"
#include "core/hle/service/set/settings_types.h"
namespace Service::Set {
struct DeviceSettings {
std::array<u8, 0x10> reserved_000;
INSERT_PADDING_BYTES(0x10); // Reserved
// nn::settings::BatteryLot
std::array<u8, 0x18> ptm_battery_lot;
@@ -19,26 +21,24 @@ struct DeviceSettings {
u8 ptm_battery_version;
// nn::settings::system::PtmCycleCountReliability
u32 ptm_cycle_count_reliability;
std::array<u8, 0x48> reserved_048;
INSERT_PADDING_BYTES(0x48); // Reserved
// nn::settings::system::AnalogStickUserCalibration L
std::array<u8, 0x10> analog_user_stick_calibration_l;
// nn::settings::system::AnalogStickUserCalibration R
std::array<u8, 0x10> analog_user_stick_calibration_r;
std::array<u8, 0x20> reserved_0B0;
INSERT_PADDING_BYTES(0x20); // Reserved
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
std::array<u8, 0xC> console_six_axis_sensor_acceleration_bias;
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_bias;
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_time_bias;
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
};

View File

@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/set/private_settings.h"
#include "core/hle/service/set/setting_formats/private_settings.h"
namespace Service::Set {

Some files were not shown because too many files have changed in this diff Show More