From 8e4af5c8233952ce8473ba1579263d16253322ae Mon Sep 17 00:00:00 2001 From: TuxSH Date: Mon, 3 Jun 2019 00:43:44 +0200 Subject: [PATCH] Add ntp client & rtc sync --- .../rosalina/include/menus/miscellaneous.h | 1 + sysmodules/rosalina/include/minisoc.h | 1 + sysmodules/rosalina/include/ntp.h | 33 +++ .../rosalina/source/menus/miscellaneous.c | 88 +++++++- sysmodules/rosalina/source/minisoc.c | 41 ++++ sysmodules/rosalina/source/ntp.c | 192 ++++++++++++++++++ 6 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 sysmodules/rosalina/include/ntp.h create mode 100644 sysmodules/rosalina/source/ntp.c diff --git a/sysmodules/rosalina/include/menus/miscellaneous.h b/sysmodules/rosalina/include/menus/miscellaneous.h index 211a654..638d660 100644 --- a/sysmodules/rosalina/include/menus/miscellaneous.h +++ b/sysmodules/rosalina/include/menus/miscellaneous.h @@ -35,3 +35,4 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void); void MiscellaneousMenu_ChangeMenuCombo(void); void MiscellaneousMenu_SaveSettings(void); void MiscellaneousMenu_InputRedirection(void); +void MiscellaneousMenu_SyncTimeDate(void); \ No newline at end of file diff --git a/sysmodules/rosalina/include/minisoc.h b/sysmodules/rosalina/include/minisoc.h index 271ae11..2d006b6 100644 --- a/sysmodules/rosalina/include/minisoc.h +++ b/sysmodules/rosalina/include/minisoc.h @@ -33,6 +33,7 @@ int socSocket(int domain, int type, int protocol); int socBind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int socListen(int sockfd, int max_connections); int socAccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +int socConnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int socPoll(struct pollfd *fds, nfds_t nfds, int timeout); int socSetsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); int socClose(int sockfd); diff --git a/sysmodules/rosalina/include/ntp.h b/sysmodules/rosalina/include/ntp.h new file mode 100644 index 0000000..8780d0d --- /dev/null +++ b/sysmodules/rosalina/include/ntp.h @@ -0,0 +1,33 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2019 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include <3ds/types.h> +#include + +Result ntpGetTimeStamp(time_t *outTimestamp); +Result ntpSetTimeDate(const struct tm *localt); \ No newline at end of file diff --git a/sysmodules/rosalina/source/menus/miscellaneous.c b/sysmodules/rosalina/source/menus/miscellaneous.c index 82530ff..99052fb 100644 --- a/sysmodules/rosalina/source/menus/miscellaneous.c +++ b/sysmodules/rosalina/source/menus/miscellaneous.c @@ -27,6 +27,7 @@ #include <3ds.h> #include "menus/miscellaneous.h" #include "input_redirection.h" +#include "ntp.h" #include "memory.h" #include "draw.h" #include "hbloader.h" @@ -38,11 +39,12 @@ Menu miscellaneousMenu = { "Miscellaneous options menu", - .nbItems = 4, + .nbItems = 5, { { "Switch the hb. title to the current app.", METHOD, .method = &MiscellaneousMenu_SwitchBoot3dsxTargetTitle }, - { "Change the menu combo", METHOD, .method = MiscellaneousMenu_ChangeMenuCombo }, + { "Change the menu combo", METHOD, .method = &MiscellaneousMenu_ChangeMenuCombo }, { "Start InputRedirection", METHOD, .method = &MiscellaneousMenu_InputRedirection }, + { "Sync time and date via NTP", METHOD, .method = &MiscellaneousMenu_SyncTimeDate }, { "Save settings", METHOD, .method = &MiscellaneousMenu_SaveSettings }, } }; @@ -316,3 +318,85 @@ void MiscellaneousMenu_InputRedirection(void) } while(!(waitInput() & BUTTON_B) && !terminationRequest); } + +void MiscellaneousMenu_SyncTimeDate(void) +{ + u32 posY; + u32 input = 0; + + Result res; + bool cantStart = false; + + bool isSocURegistered; + + time_t t; + struct tm localt = {0}; + + res = srvIsServiceRegistered(&isSocURegistered, "soc:U"); + cantStart = R_FAILED(res) || !isSocURegistered; + + int utcOffset = 12; + int absOffset; + do + { + Draw_Lock(); + Draw_DrawString(10, 10, COLOR_TITLE, "Miscellaneous options menu"); + + //posY = Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Current UTC offset: "); + absOffset = utcOffset - 12; + absOffset = absOffset < 0 ? -absOffset : absOffset; + posY = Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Current UTC offset: %c%02d", utcOffset < 12 ? '-' : '+', absOffset); + posY = Draw_DrawFormattedString(10, posY + SPACING_Y, COLOR_WHITE, "Use DPAD Left/Right to change offset.\nPress A when done.") + SPACING_Y; + + input = waitInput(); + + if(input & BUTTON_LEFT) utcOffset = (24 + utcOffset - 1) % 24; // ensure utcOffset >= 0 + if(input & BUTTON_RIGHT) utcOffset = (utcOffset + 1) % 24; + + Draw_FlushFramebuffer(); + Draw_Unlock(); + } + while(!(input & (BUTTON_A | BUTTON_B)) && !terminationRequest); + + if (input & BUTTON_B) + return; + + utcOffset -= 12; + + res = srvIsServiceRegistered(&isSocURegistered, "soc:U"); + cantStart = R_FAILED(res) || !isSocURegistered; + res = 0; + if(!cantStart) + { + res = ntpGetTimeStamp(&t); + if(R_SUCCEEDED(res)) + { + t += 3600 * utcOffset; + gmtime_r(&t, &localt); + res = ntpSetTimeDate(&localt); + } + } + + do + { + Draw_Lock(); + Draw_DrawString(10, 10, COLOR_TITLE, "Miscellaneous options menu"); + + absOffset = utcOffset; + absOffset = absOffset < 0 ? -absOffset : absOffset; + Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Current UTC offset: %c%02d", utcOffset < 0 ? '-' : '+', absOffset); + if (cantStart) + posY = Draw_DrawFormattedString(10, posY + 2 * SPACING_Y, COLOR_WHITE, "Can't sync time/date before the system\nhas finished loading.") + SPACING_Y; + else if (R_FAILED(res)) + posY = Draw_DrawFormattedString(10, posY + 2 * SPACING_Y, COLOR_WHITE, "Operation failed (%08lx).", (u32)res) + SPACING_Y; + else + posY = Draw_DrawFormattedString(10, posY + 2 * SPACING_Y, COLOR_WHITE, "Timedate & RTC updated successfully.\nYou may need to reboot to see the changes.") + SPACING_Y; + + input = waitInput(); + + Draw_FlushFramebuffer(); + Draw_Unlock(); + } + while(!(input & BUTTON_B) && !terminationRequest); + +} \ No newline at end of file diff --git a/sysmodules/rosalina/source/minisoc.c b/sysmodules/rosalina/source/minisoc.c index ccdc6e8..2b81159 100644 --- a/sysmodules/rosalina/source/minisoc.c +++ b/sysmodules/rosalina/source/minisoc.c @@ -312,6 +312,47 @@ int socAccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) return ret; } +int socConnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + int ret = 0; + socklen_t tmp_addrlen = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + u8 tmpaddr[0x1c]; + + memset(tmpaddr, 0, 0x1c); + + if(addr->sa_family == AF_INET) + tmp_addrlen = 8; + else + tmp_addrlen = 0x1c; + + if(addrlen < tmp_addrlen) + return -1; + + tmpaddr[0] = tmp_addrlen; + tmpaddr[1] = addr->sa_family; + memcpy(&tmpaddr[2], &addr->sa_data, tmp_addrlen-2); + + cmdbuf[0] = IPC_MakeHeader(0x6,2,4); // 0x60084 + cmdbuf[1] = (u32)sockfd; + cmdbuf[2] = (u32)addrlen; + cmdbuf[3] = IPC_Desc_CurProcessId(); + cmdbuf[5] = IPC_Desc_StaticBuffer(tmp_addrlen,0); + cmdbuf[6] = (u32)tmpaddr; + + ret = svcSendSyncRequest(SOCU_handle); + if(ret != 0) return -1; + + ret = (int)cmdbuf[1]; + if(ret == 0) + ret = _net_convert_error(cmdbuf[2]); + + if(ret < 0) + return -1; + + return 0; +} + int socPoll(struct pollfd *fds, nfds_t nfds, int timeout) { int ret = 0; diff --git a/sysmodules/rosalina/source/ntp.c b/sysmodules/rosalina/source/ntp.c new file mode 100644 index 0000000..ebdee25 --- /dev/null +++ b/sysmodules/rosalina/source/ntp.c @@ -0,0 +1,192 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2019 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include <3ds.h> +#include +#include +#include "utils.h" +#include "minisoc.h" + +#define NUM2BCD(n) ((n<99) ? (((n/10)*0x10)|(n%10)) : 0x99) + +#define NTP_TIMESTAMP_DELTA 2208988800ull + +typedef struct RtcTime { + // From 3dbrew + u8 seconds; + u8 minutes; + u8 hours; + u8 dayofweek; + u8 dayofmonth; + u8 month; + u8 year; + u8 leapcount; +} RtcTime; + +// From https://github.com/lettier/ntpclient/blob/master/source/c/main.c + +typedef struct NtpPacket +{ + + u8 li_vn_mode; // Eight bits. li, vn, and mode. + // li. Two bits. Leap indicator. + // vn. Three bits. Version number of the protocol. + // mode. Three bits. Client will pick mode 3 for client. + + u8 stratum; // Eight bits. Stratum level of the local clock. + u8 poll; // Eight bits. Maximum interval between successive messages. + u8 precision; // Eight bits. Precision of the local clock. + + u32 rootDelay; // 32 bits. Total round trip delay time. + u32 rootDispersion; // 32 bits. Max error aloud from primary clock source. + u32 refId; // 32 bits. Reference clock identifier. + + u32 refTm_s; // 32 bits. Reference time-stamp seconds. + u32 refTm_f; // 32 bits. Reference time-stamp fraction of a second. + + u32 origTm_s; // 32 bits. Originate time-stamp seconds. + u32 origTm_f; // 32 bits. Originate time-stamp fraction of a second. + + u32 rxTm_s; // 32 bits. Received time-stamp seconds. + u32 rxTm_f; // 32 bits. Received time-stamp fraction of a second. + + u32 txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds. + u32 txTm_f; // 32 bits. Transmit time-stamp fraction of a second. + +} NtpPacket; // Total: 384 bits or 48 bytes. + +void rtcToBcd(u8 *out, const RtcTime *in) +{ + memcpy(out, in, 8); + for (u32 i = 0; i < 8; i++) + { + u8 units = out[i] % 10; + u8 tens = (out[i] - units) / 10; + out[i] = (tens << 4) | units; + } +} + +Result ntpGetTimeStamp(time_t *outTimestamp) +{ + Result res = 0; + struct linger linger; + res = miniSocInit(); + if(R_FAILED(res)) + return res; + + int sock = socSocket(AF_INET, SOCK_DGRAM, 0); + struct sockaddr_in servAddr = {0}; // Server address data structure. + NtpPacket packet = {0}; + + // Set the first byte's bits to 00,011,011 for li = 0, vn = 3, and mode = 3. The rest will be left set to zero. + packet.li_vn_mode = 0x1b; + + // Zero out the server address structure. + servAddr.sin_family = AF_INET; + + // Copy the server's IP address to the server address structure. + + servAddr.sin_addr.s_addr = htonl(0xD8EF2300); // 216.239.35.0 time1.google.com + // Convert the port number integer to network big-endian style and save it to the server address structure. + + servAddr.sin_port = htons(123); + + // Call up the server using its IP address and port number. + res = -1; + if(socConnect(sock, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) < 0) + goto cleanup; + + if(soc_send(sock, &packet, sizeof(NtpPacket), 0) < 0) + goto cleanup; + + if(soc_recv(sock, &packet, sizeof(NtpPacket), 0) < 0) + goto cleanup; + + res = 0; + + // These two fields contain the time-stamp seconds as the packet left the NTP server. + // The number of seconds correspond to the seconds passed since 1900. + // 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); + +cleanup: + linger.l_onoff = 1; + linger.l_linger = 0; + socSetsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger)); + + socClose(sock); + miniSocExit(); + + return res; +} + +Result ntpSetTimeDate(const struct tm *localt) +{ + Result res = mcuHwcInit(); + if (R_FAILED(res)) return res; + + + res = cfguInit(); + if (R_FAILED(res)) goto cleanup; + + // First, set the config RTC offset to 0 + u8 rtcOff = 0; + res = CFG_SetConfigInfoBlk4(1, 0x10000, &rtcOff); + if (R_FAILED(res)) goto cleanup; + + u8 yr = (u8)(localt->tm_year - 100); + // Update the RTC + u8 bcd[8]; + RtcTime lt = { + .seconds = (u8)localt->tm_sec, + .minutes = (u8)localt->tm_min, + .hours = (u8)localt->tm_hour, + .dayofweek = (u8)localt->tm_wday, + .dayofmonth = (u8)localt->tm_mday, + .month = (u8)(localt->tm_mon + 1), + .year = yr, + .leapcount = 0, + }; + rtcToBcd(bcd, <); + + res = MCUHWC_WriteRegister(0x30, bcd, 7); + if (R_FAILED(res)) goto cleanup; + + // Save the config changes + res = CFG_UpdateConfigSavegame(); + cleanup: + mcuHwcExit(); + cfguExit(); + return res; +} \ No newline at end of file