diff --git a/sysmodules/rosalina/include/ntp.h b/sysmodules/rosalina/include/ntp.h index a4a7fd7..cd2a0bb 100644 --- a/sysmodules/rosalina/include/ntp.h +++ b/sysmodules/rosalina/include/ntp.h @@ -29,6 +29,6 @@ #include <3ds/types.h> #include -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 diff --git a/sysmodules/rosalina/source/menus/miscellaneous.c b/sysmodules/rosalina/source/menus/miscellaneous.c index 099fb93..ce0e6e2 100644 --- a/sysmodules/rosalina/source/menus/miscellaneous.c +++ b/sysmodules/rosalina/source/menus/miscellaneous.c @@ -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); } } diff --git a/sysmodules/rosalina/source/ntp.c b/sysmodules/rosalina/source/ntp.c index 6508bf5..6e136d0 100644 --- a/sysmodules/rosalina/source/ntp.c +++ b/sysmodules/rosalina/source/ntp.c @@ -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); +}