aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs
blob: f8e36bcd721da46d745d0c96cc5fad765a8d26ca (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using System;
using System.IO;

namespace Ryujinx.HLE.HOS.Services.Time
{
    class TimeManager
    {
        private static TimeManager _instance;

        public static TimeManager Instance
        {
            get
            {
                _instance ??= new TimeManager();

                return _instance;
            }
        }

        public StandardSteadyClockCore StandardSteadyClock { get; }
        public TickBasedSteadyClockCore TickBasedSteadyClock { get; }
        public StandardLocalSystemClockCore StandardLocalSystemClock { get; }
        public StandardNetworkSystemClockCore StandardNetworkSystemClock { get; }
        public StandardUserSystemClockCore StandardUserSystemClock { get; }
        public TimeZoneContentManager TimeZone { get; }
        public EphemeralNetworkSystemClockCore EphemeralNetworkSystemClock { get; }
        public TimeSharedMemory SharedMemory { get; }
        public LocalSystemClockContextWriter LocalClockContextWriter { get; }
        public NetworkSystemClockContextWriter NetworkClockContextWriter { get; }
        public EphemeralNetworkSystemClockContextWriter EphemeralClockContextWriter { get; }

        // TODO: 9.0.0+ power states and alarms

        public TimeManager()
        {
            StandardSteadyClock = new StandardSteadyClockCore();
            TickBasedSteadyClock = new TickBasedSteadyClockCore();
            StandardLocalSystemClock = new StandardLocalSystemClockCore(StandardSteadyClock);
            StandardNetworkSystemClock = new StandardNetworkSystemClockCore(StandardSteadyClock);
            StandardUserSystemClock = new StandardUserSystemClockCore(StandardLocalSystemClock, StandardNetworkSystemClock);
            TimeZone = new TimeZoneContentManager();
            EphemeralNetworkSystemClock = new EphemeralNetworkSystemClockCore(TickBasedSteadyClock);
            SharedMemory = new TimeSharedMemory();
            LocalClockContextWriter = new LocalSystemClockContextWriter(SharedMemory);
            NetworkClockContextWriter = new NetworkSystemClockContextWriter(SharedMemory);
            EphemeralClockContextWriter = new EphemeralNetworkSystemClockContextWriter();
        }

        public void Initialize(Switch device, Horizon system, KSharedMemory sharedMemory, SharedMemoryStorage timeSharedMemoryStorage, int timeSharedMemorySize)
        {
            SharedMemory.Initialize(device, sharedMemory, timeSharedMemoryStorage, timeSharedMemorySize);

            // Here we use system on purpose as device. System isn't initialized at this point.
            StandardUserSystemClock.CreateAutomaticCorrectionEvent(system);
        }

        public void InitializeTimeZone(Switch device)
        {
            TimeZone.Initialize(this, device);
        }

        public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType setupValue, TimeSpanType internalOffset, TimeSpanType testOffset, bool isRtcResetDetected)
        {
            SetupInternalStandardSteadyClock(clockSourceId, setupValue, internalOffset, testOffset, isRtcResetDetected);

            TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(tickSource);

            SharedMemory.SetupStandardSteadyClock(tickSource, clockSourceId, currentTimePoint);

            // TODO: propagate IPC late binding of "time:s" and "time:p"
        }

        private void SetupInternalStandardSteadyClock(UInt128 clockSourceId, TimeSpanType setupValue, TimeSpanType internalOffset, TimeSpanType testOffset, bool isRtcResetDetected)
        {
            StandardSteadyClock.SetClockSourceId(clockSourceId);
            StandardSteadyClock.SetSetupValue(setupValue);
            StandardSteadyClock.SetInternalOffset(internalOffset);
            StandardSteadyClock.SetTestOffset(testOffset);

            if (isRtcResetDetected)
            {
                StandardSteadyClock.SetRtcReset();
            }

            StandardSteadyClock.MarkInitialized();

            // TODO: propagate IPC late binding of "time:s" and "time:p"
        }

        public void SetupStandardLocalSystemClock(ITickSource tickSource, SystemClockContext clockContext, long posixTime)
        {
            StandardLocalSystemClock.SetUpdateCallbackInstance(LocalClockContextWriter);

            SteadyClockTimePoint currentTimePoint = StandardLocalSystemClock.GetSteadyClockCore().GetCurrentTimePoint(tickSource);
            if (currentTimePoint.ClockSourceId == clockContext.SteadyTimePoint.ClockSourceId)
            {
                StandardLocalSystemClock.SetSystemClockContext(clockContext);
            }
            else
            {
                if (StandardLocalSystemClock.SetCurrentTime(tickSource, posixTime) != ResultCode.Success)
                {
                    throw new InternalServiceException("Cannot set current local time");
                }
            }

            StandardLocalSystemClock.MarkInitialized();

            // TODO: propagate IPC late binding of "time:s" and "time:p"
        }

        public void SetupStandardNetworkSystemClock(SystemClockContext clockContext, TimeSpanType sufficientAccuracy)
        {
            StandardNetworkSystemClock.SetUpdateCallbackInstance(NetworkClockContextWriter);

            if (StandardNetworkSystemClock.SetSystemClockContext(clockContext) != ResultCode.Success)
            {
                throw new InternalServiceException("Cannot set network SystemClockContext");
            }

            StandardNetworkSystemClock.SetStandardNetworkClockSufficientAccuracy(sufficientAccuracy);
            StandardNetworkSystemClock.MarkInitialized();

            // TODO: propagate IPC late binding of "time:s" and "time:p"
        }

        public void SetupTimeZoneManager(string locationName, SteadyClockTimePoint timeZoneUpdatedTimePoint, uint totalLocationNameCount, UInt128 timeZoneRuleVersion, Stream timeZoneBinaryStream)
        {
            if (TimeZone.Manager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream) != ResultCode.Success)
            {
                throw new InternalServiceException("Cannot set DeviceLocationName with a given TimeZoneBinary");
            }

            TimeZone.Manager.SetUpdatedTime(timeZoneUpdatedTimePoint, true);
            TimeZone.Manager.SetTotalLocationNameCount(totalLocationNameCount);
            TimeZone.Manager.SetTimeZoneRuleVersion(timeZoneRuleVersion);
            TimeZone.Manager.MarkInitialized();

            // TODO: propagate IPC late binding of "time:s" and "time:p"
        }

        public void SetupEphemeralNetworkSystemClock()
        {
            EphemeralNetworkSystemClock.SetUpdateCallbackInstance(EphemeralClockContextWriter);
            EphemeralNetworkSystemClock.MarkInitialized();

            // TODO: propagate IPC late binding of "time:s" and "time:p"
        }

        public void SetupStandardUserSystemClock(ITickSource tickSource, bool isAutomaticCorrectionEnabled, SteadyClockTimePoint steadyClockTimePoint)
        {
            if (StandardUserSystemClock.SetAutomaticCorrectionEnabled(tickSource, isAutomaticCorrectionEnabled) != ResultCode.Success)
            {
                throw new InternalServiceException("Cannot set automatic user time correction state");
            }

            StandardUserSystemClock.SetAutomaticCorrectionUpdatedTime(steadyClockTimePoint);
            StandardUserSystemClock.MarkInitialized();

            SharedMemory.SetAutomaticCorrectionEnabled(isAutomaticCorrectionEnabled);

            // TODO: propagate IPC late binding of "time:s" and "time:p"
        }

        public void SetStandardSteadyClockRtcOffset(ITickSource tickSource, TimeSpanType rtcOffset)
        {
            StandardSteadyClock.SetSetupValue(rtcOffset);

            TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(tickSource);

            SharedMemory.SetSteadyClockRawTimePoint(tickSource, currentTimePoint);
        }
    }
}