ntp: more precise & also set system time

System time is what System Settings (only) changes; user time seems to be set periodically by HM.
This commit is contained in:
TuxSH 2022-03-15 20:24:49 +00:00
parent 44c1711805
commit bc6e14ada7
3 changed files with 71 additions and 51 deletions

View File

@ -29,6 +29,6 @@
#include <3ds/types.h>
#include <time.h>
Result ntpGetTimeStamp(time_t *outTimestamp);
Result ntpSetTimeDate(time_t timestamp);
Result ntpGetTimeStamp(u64 *msSince1900, u64 *samplingTick);
Result ntpSetTimeDate(u64 msSince1900, u64 samplingTick);
Result ntpNullifyUserTimeOffset(void); // not actually used for NTP

View File

@ -385,7 +385,7 @@ void MiscellaneousMenu_UpdateTimeDateNtp(void)
bool isSocURegistered;
time_t t;
u64 msSince1900, samplingTick;
res = srvIsServiceRegistered(&isSocURegistered, "soc:U");
cantStart = R_FAILED(res) || !isSocURegistered;
@ -424,12 +424,11 @@ void MiscellaneousMenu_UpdateTimeDateNtp(void)
res = 0;
if(!cantStart)
{
res = ntpGetTimeStamp(&t);
res = ntpGetTimeStamp(&msSince1900, &samplingTick);
if(R_SUCCEEDED(res))
{
t += 3600 * utcOffset;
t += 60 * utcOffsetMinute;
res = ntpSetTimeDate(t);
msSince1900 += 1000 * (3600 * utcOffset + 60 * utcOffsetMinute);
res = ntpSetTimeDate(msSince1900, samplingTick);
}
}

View File

@ -33,7 +33,9 @@
#define NUM2BCD(n) ((n<99) ? (((n/10)*0x10)|(n%10)) : 0x99)
#define NTP_TIMESTAMP_DELTA 2208988800ull
//#define NTP_TIMESTAMP_DELTA 2208988800ull
#define MSEC_DELTA_1900_2000 3155673600000ull
#define MAKE_IPV4(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
@ -73,10 +75,13 @@ typedef struct NtpPacket
} NtpPacket; // Total: 384 bits or 48 bytes.
Result ntpGetTimeStamp(time_t *outTimestamp)
Result ntpGetTimeStamp(u64 *msSince1900, u64 *samplingTick)
{
Result res = 0;
struct linger linger;
*msSince1900 = 0;
*samplingTick = 0;
res = miniSocInit();
if(R_FAILED(res))
return res;
@ -108,12 +113,14 @@ Result ntpGetTimeStamp(time_t *outTimestamp)
if(socConnect(sock, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) < 0)
goto cleanup;
u64 roundTripTicks = svcGetSystemTick();
if(socSend(sock, &packet, sizeof(NtpPacket), 0) < 0)
goto cleanup;
if(socRecv(sock, &packet, sizeof(NtpPacket), 0) < 0)
goto cleanup;
roundTripTicks = svcGetSystemTick() - roundTripTicks;
u64 dt = 1000 * 1000 * roundTripTicks / (2 * SYSCLOCK_ARM11); // avg = round trip time / 2
res = 0;
// These two fields contain the time-stamp seconds as the packet left the NTP server.
@ -121,13 +128,11 @@ Result ntpGetTimeStamp(time_t *outTimestamp)
// ntohl() converts the bit/byte order from the network's to host's "endianness".
packet.txTm_s = ntohl(packet.txTm_s); // Time-stamp seconds.
packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second.
// Extract the 32 bits that represent the time-stamp seconds (since NTP epoch) from when the packet left the server.
// Subtract 70 years worth of seconds from the seconds since 1900.
// This leaves the seconds since the UNIX epoch of 1970.
// (1900)------------------(1970)**************************************(Time Packet Left the Server)
*outTimestamp = (time_t)(packet.txTm_s - NTP_TIMESTAMP_DELTA);
packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second. txTm is 32.32 fixed point.
u64 txTmUsec = (1000 * 1000 * (u64)packet.txTm_f) >> 32; // convert txTm to usec (truncate; end result is in ms anyway)
txTmUsec += 1000 * 1000 * (u64)packet.txTm_s;
*msSince1900 = (txTmUsec + dt + 500u) / 1000u;
*samplingTick = svcGetSystemTick();
cleanup:
linger.l_onoff = 1;
@ -140,54 +145,70 @@ cleanup:
return res;
}
Result ntpSetTimeDate(time_t timestamp)
static Result ntpSetTimeDateImpl(u64 msSince1900, u64 samplingTick, bool syncRtc)
{
Result res = ptmSysmInit();
if (R_FAILED(res)) return res;
// Update the user time offset
// 946684800 is the timestamp of 01/01/2000 00:00 relative to the Unix Epoch
s64 msY2k = (timestamp - 946684800) * 1000;
res = PTMSYSM_SetUserTime(msY2k);
ptmSysmExit();
return res;
}
// Not actually used for NTP, but...
Result ntpNullifyUserTimeOffset(void)
{
Result res = ptmSysmInit();
if (R_FAILED(res)) return res;
res = cfguInit();
res = ptmSetsInit();
if (R_FAILED(res))
{
ptmSysmExit();
return res;
}
res = cfguInit();
if (R_FAILED(res))
{
ptmSetsExit();
ptmSysmExit();
return res;
}
// First, set the user time offset to 0 (user time = rtc time + user time offset)
s64 userTimeOff = 0;
res = CFG_SetConfigInfoBlk4(8, 0x30001, &userTimeOff);
if (R_FAILED(res)) goto cleanup;
u64 dt = 1000 * (svcGetSystemTick() - samplingTick) / SYSCLOCK_ARM11;
s64 msY2k = msSince1900 + dt - MSEC_DELTA_1900_2000;
// Get the user time from shared data... there might be up to 0.5s drift from {mcu+offset} but we don't care here
s64 userTime = osGetTime() - 3155673600000LL; // 1900 -> 2000 time base
if (syncRtc)
{
u64 samplingTick2 = svcGetSystemTick();
// Apply user time to RTC
res = PTMSYSM_SetRtcTime(userTime);
if (R_FAILED(res)) goto cleanup;
s64 timeOff = 0;
res = CFG_SetConfigInfoBlk4(8, 0x30001, &timeOff);
if (R_SUCCEEDED(res)) res = CFG_SetConfigInfoBlk4(8, 0x30002, &timeOff);
// Invalidate system (absolute, server) time, which gets fixed on "friends" login anyway -- don't care if we fail here.
// It has become invalid because we changed the RTC time
PTMSYSM_InvalidateSystemTime();
// Save the config changes
if (R_SUCCEEDED(res)) res = CFG_UpdateConfigSavegame();
// Wait till next second
msY2k += 1000 * (svcGetSystemTick() - samplingTick2) / SYSCLOCK_ARM11;
if (msY2k % 1000u != 0)
{
u64 dt2 = 1000u - msY2k % 1000u;
svcSleepThread(dt2);
msY2k += dt2;
}
if (R_SUCCEEDED(res)) res = PTMSYSM_SetRtcTime(msY2k);
}
else
{
if (R_SUCCEEDED(res)) res = PTMSYSM_SetUserTime(msY2k);
if (R_SUCCEEDED(res)) res = PTMSETS_SetSystemTime(msY2k);
}
// Save the config changes
res = CFG_UpdateConfigSavegame();
cleanup:
ptmSysmExit();
cfguExit();
ptmSetsExit();
ptmSysmExit();
return res;
}
Result ntpSetTimeDate(u64 msSince1900, u64 samplingTick)
{
return ntpSetTimeDateImpl(msSince1900, samplingTick, false);
}
// Not actually used for NTP, but...
Result ntpNullifyUserTimeOffset(void)
{
u64 msSince1900 = osGetTime();
u64 samplingTick = svcGetSystemTick();
return ntpSetTimeDateImpl(msSince1900, samplingTick, true);
}