diff --git a/nx/include/switch.h b/nx/include/switch.h index 006fa1f0..2745edc7 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -35,6 +35,7 @@ extern "C" { #include "switch/services/audout.h" #include "switch/services/bsd.h" #include "switch/services/fatal.h" +#include "switch/services/time.h" #include "switch/services/usb.h" #include "switch/services/hid.h" #include "switch/services/irs.h" diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index 3827152d..3299aa6e 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -64,6 +64,7 @@ enum { LibnxError_JitUnavailable, LibnxError_WeirdKernel, LibnxError_IncompatSysVer, + LibnxError_InitFail_Time, }; /// libnx nvidia error codes diff --git a/nx/include/switch/services/time.h b/nx/include/switch/services/time.h new file mode 100644 index 00000000..eb3ed396 --- /dev/null +++ b/nx/include/switch/services/time.h @@ -0,0 +1,24 @@ +/** + * @file time.h + * @brief Time services IPC wrapper. + * @author yellows8 + * @copyright libnx Authors + */ +#pragma once + +#include "../types.h" +#include "../services/sm.h" + +typedef enum { + TimeType_UserSystemClock, + TimeType_NetworkSystemClock, + TimeType_LocalSystemClock, + TimeType_Default = TimeType_NetworkSystemClock, +} TimeType; + +Result timeInitialize(void); +void timeExit(void); + +Service* timeGetSessionService(void); + +Result timeGetCurrentTime(TimeType type, u64 *timestamp); diff --git a/nx/source/runtime/init.c b/nx/source/runtime/init.c index d3224465..d034c67b 100644 --- a/nx/source/runtime/init.c +++ b/nx/source/runtime/init.c @@ -4,6 +4,7 @@ #include "services/fatal.h" #include "services/fs.h" #include "services/hid.h" +#include "services/time.h" #include "services/applet.h" #include "runtime/devices/fs_dev.h" @@ -101,6 +102,10 @@ void __attribute__((weak)) __appInit(void) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_HID)); } + rc = timeInitialize(); + if (R_FAILED(rc)) + fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_Time)); + rc = fsInitialize(); if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); @@ -113,6 +118,7 @@ void __attribute__((weak)) __appExit(void) // Cleanup default services. fsdevExit(); fsExit(); + timeExit(); hidExit(); appletExit(); smExit(); diff --git a/nx/source/runtime/newlib.c b/nx/source/runtime/newlib.c index dccc888e..561ef34e 100644 --- a/nx/source/runtime/newlib.c +++ b/nx/source/runtime/newlib.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -8,6 +9,7 @@ #include "runtime/env.h" #include "kernel/mutex.h" #include "services/fatal.h" +#include "services/time.h" #include "result.h" void __attribute__((weak)) NORETURN __libnx_exit(int rc); @@ -16,6 +18,9 @@ extern const u8 __tdata_lma[]; extern const u8 __tdata_lma_end[]; extern u8 __tls_start[]; +/// TimeType passed to timeGetCurrentTime() by __libnx_gtod(). +__attribute__((weak)) TimeType __nx_time_type = TimeType_Default; + static struct _reent* __libnx_get_reent(void) { ThreadVars* tv = getThreadVars(); if (tv->magic != THREADVARS_MAGIC) @@ -23,9 +28,33 @@ static struct _reent* __libnx_get_reent(void) { return tv->reent; } +int __libnx_gtod(struct _reent *ptr, struct timeval *tp, struct timezone *tz) { + if (tp != NULL) { + u64 now=0; + Result rc=0; + + rc = timeGetCurrentTime(__nx_time_type, &now); + if (R_FAILED(rc)) { + ptr->_errno = EINVAL; + return -1; + } + + tp->tv_sec = now; + tp->tv_usec = 0;//timeGetCurrentTime() only returns seconds. + } + + if (tz != NULL) {//TODO: This needs handled properly, timeGetCurrentTime() returns UTC time. + tz->tz_minuteswest = 0; + tz->tz_dsttime = 0; + } + + return 0; +} + void newlibSetup(void) { // Register newlib syscalls __syscalls.exit = __libnx_exit; + __syscalls.gettod_r = __libnx_gtod; __syscalls.getreent = __libnx_get_reent; // Register locking syscalls diff --git a/nx/source/services/time.c b/nx/source/services/time.c new file mode 100644 index 00000000..30e502e1 --- /dev/null +++ b/nx/source/services/time.c @@ -0,0 +1,143 @@ +#include +#include "types.h" +#include "result.h" +#include "ipc.h" +#include "services/time.h" +#include "services/sm.h" + +static Service g_timeSrv; +static Service g_timeUserSystemClock; +static Service g_timeNetworkSystemClock; +static Service g_timeTimeZoneService; +static Service g_timeLocalSystemClock; + +static Result _timeGetSession(Service* srv_out, u64 cmd_id); + +Result timeInitialize(void) +{ + if (serviceIsActive(&g_timeSrv)) + return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); + + Result rc; + + rc = smGetService(&g_timeSrv, "time:u"); + if (R_FAILED(rc)) + return rc; + + rc = _timeGetSession(&g_timeUserSystemClock, 0); + + if (R_SUCCEEDED(rc)) + rc = _timeGetSession(&g_timeNetworkSystemClock, 1); + + if (R_SUCCEEDED(rc)) + rc = _timeGetSession(&g_timeTimeZoneService, 3); + + if (R_SUCCEEDED(rc)) + rc = _timeGetSession(&g_timeLocalSystemClock, 4); + + if (R_FAILED(rc)) + timeExit(); + + return rc; +} + +void timeExit(void) +{ + if (!serviceIsActive(&g_timeSrv)) + return; + + serviceClose(&g_timeLocalSystemClock); + serviceClose(&g_timeTimeZoneService); + serviceClose(&g_timeNetworkSystemClock); + serviceClose(&g_timeUserSystemClock); + serviceClose(&g_timeSrv); +} + +Service* timeGetSessionService(void) { + return &g_timeSrv; +} + +static Result _timeGetSession(Service* srv_out, u64 cmd_id) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = serviceIpcDispatch(&g_timeSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(srv_out, r.Handles[0]); + } + } + + return rc; +} + +Result timeGetCurrentTime(TimeType type, u64 *timestamp) { + Service *srv = NULL; + + if (type==TimeType_UserSystemClock) { + srv = &g_timeUserSystemClock; + } + else if (type==TimeType_NetworkSystemClock) { + srv = &g_timeNetworkSystemClock; + } + else if (type==TimeType_LocalSystemClock) { + srv = &g_timeLocalSystemClock; + } + else { + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 timestamp; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && timestamp) *timestamp = resp->timestamp; + } + + return rc; +} +