diff --git a/nx/include/switch.h b/nx/include/switch.h index 127993db8..df26430ba 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -133,6 +133,7 @@ extern "C" { #include "switch/services/avm.h" #include "switch/services/mm.h" #include "switch/services/omm.h" +#include "switch/services/ovln.h" #include "switch/display/binder.h" #include "switch/display/parcel.h" diff --git a/nx/include/switch/services/ovln.h b/nx/include/switch/services/ovln.h new file mode 100644 index 000000000..5fefe5583 --- /dev/null +++ b/nx/include/switch/services/ovln.h @@ -0,0 +1,153 @@ +/** + * @file ovln.h + * @brief Overlay notification (ovln:*) service IPC wrapper. + * @author ndeadly + * @copyright libnx Authors + */ +#pragma once +#include "../sf/service.h" +#include "../kernel/event.h" + +/// EnqueuePosition +typedef enum { + OvlnEnqueuePosition_Front = 0, + OvlnEnqueuePosition_Back = 1, +} OvlnEnqueuePosition; + +/// OverflowOption +typedef enum { + OvlnOverflowOption_Error = 0, + OvlnOverflowOption_RemoveFront = 1, + OvlnOverflowOption_RemoveBack = 2, + OvlnOverflowOption_Block = 3, +} OvlnOverflowOption; + +/// SourceName +typedef struct { + char name[0x10]; ///< Source name. Official software always uses the name "overlay". +} OvlnSourceName; + +/// RawMessage +typedef struct { + u32 tag; ///< Unique tag specifying the message type. + u32 data_size; ///< Size of the below data. + u8 data[0x78]; ///< Message data. +} OvlnRawMessage; + +/// QueueAttribute +typedef struct { + u32 queue_length; ///< Length of send queue. + u8 reserved[4]; ///< Reserved. +} OvlnQueueAttribute; + +/// SendOption +typedef struct { + u8 enqueue_position; ///< \ref OvlnEnqueuePosition + u8 overflow_option; ///< \ref OvlnOverflowOption + u8 reserved[6]; ///< Reserved. +} OvlnSendOption; + +/// Receiver +typedef struct { + Service s; ///< IReceiver +} OvlnReceiver; + +/// Sender +typedef struct { + Service s; ///< ISender +} OvlnSender; + +/// Initialize ovln:rcv. +Result ovlnrcvInitialize(void); + +/// Exit ovln:rcv. +void ovlnrcvExit(void); + +/// Initialize ovln:snd. +Result ovlnsndInitialize(void); + +/// Exit ovln:snd. +void ovlnsndExit(void); + +/// Gets the Service object for the actual ovln:rcv service session. +Service* ovlnrcvGetServiceSession(void); + +/// Gets the Service object for the actual ovln:snd service session. +Service* ovlnsndGetServiceSession(void); + +/** + * @brief OpenReceiver + * @param[out] r \ref OvlnReceiver + */ +Result ovlnrcvOpenReceiver(OvlnReceiver *r); + +/** + * @brief CloseReceiver + * @param[in] r \ref OvlnReceiver + */ +void ovlnrcvCloseReceiver(OvlnReceiver *r); + +/** + * @brief AddSource + * @param[in] r \ref OvlnReceiver + * @param[in] name \ref OvlnSourceName + */ +Result ovlnrcvAddSource(OvlnReceiver *r, const OvlnSourceName *name); + +/** + * @brief RemoveSource + * @param[in] r \ref OvlnReceiver + * @param[in] name \ref OvlnSourceName + */ +Result ovlnrcvRemoveSource(OvlnReceiver *r, const OvlnSourceName *name); + +/** + * @brief GetReceiveEventHandle + * @param[in] r \ref OvlnReceiver + * @param[out] out_event Output Event with autoclear=true. + */ +Result ovlnrcvGetReceiveEventHandle(OvlnReceiver *r, Event* out_event); + +/** + * @brief Receive + * @param[in] r \ref OvlnReceiver + * @param[out] message \ref OvlnRawMessage + */ +Result ovlnrcvReceive(OvlnReceiver *r, OvlnRawMessage *message); + +/** + * @brief ReceiveWithTick + * @param[in] r \ref OvlnReceiver + * @param[out] message \ref OvlnRawMessage + * @param[out] tick System tick. + */ +Result ovlnrcvReceiveWithTick(OvlnReceiver *r, OvlnRawMessage *message, s64 *tick); + +/** + * @brief OpenSender + * @param[in] s \ref OvlnSender + * @param[in] name \ref OvlnSourceName + * @param[in] attribute \ref OvlnQueueAttribute + */ +Result ovlnsndOpenSender(OvlnSender *s, const OvlnSourceName *name, OvlnQueueAttribute attribute); + +/** + * @brief CloseSender + * @param[in] s \ref OvlnSender + */ +void ovlnsndCloseSender(OvlnSender *s); + +/** + * @brief Send + * @param[in] s \ref OvlnSender + * @param[in] option \ref OvlnSendOption + * @param[in] message \ref OvlnRawMessage + */ +Result ovlnsndSend(OvlnSender *s, OvlnSendOption option, const OvlnRawMessage *message); + +/** + * @brief GetUnreceivedMessageCount + * @param[in] s \ref OvlnSender + * @param[out] count number of unreceived messages. + */ +Result ovlnsndGetUnreceivedMessageCount(OvlnSender *s, u32 *count); diff --git a/nx/source/services/ovln.c b/nx/source/services/ovln.c new file mode 100644 index 000000000..6964e2a6f --- /dev/null +++ b/nx/source/services/ovln.c @@ -0,0 +1,111 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include "service_guard.h" +#include "services/ovln.h" + +static Service g_ovlnrcvSrv; +static Service g_ovlnsndSrv; + +NX_GENERATE_SERVICE_GUARD(ovlnrcv); +NX_GENERATE_SERVICE_GUARD(ovlnsnd); + +Result _ovlnrcvInitialize(void) { + return smGetService(&g_ovlnrcvSrv, "ovln:rcv"); +} + +void _ovlnrcvCleanup(void) { + serviceClose(&g_ovlnrcvSrv); +} + +Result _ovlnsndInitialize(void) { + return smGetService(&g_ovlnsndSrv, "ovln:snd"); +} + +void _ovlnsndCleanup(void) { + serviceClose(&g_ovlnsndSrv); +} + +Service* ovlnrcvGetServiceSession(void) { + return &g_ovlnrcvSrv; +} + +Service* ovlnsndGetServiceSession(void) { + return &g_ovlnsndSrv; +} + +Result ovlnrcvOpenReceiver(OvlnReceiver *receiver) { + return serviceDispatch(&g_ovlnrcvSrv, 0, + .out_num_objects = 1, + .out_objects = &receiver->s, + ); +} + +void ovlnrcvCloseReceiver(OvlnReceiver *r) { + serviceClose(&r->s); +} + +Result ovlnrcvAddSource(OvlnReceiver *r, const OvlnSourceName *name) { + return serviceDispatchIn(&r->s, 0, *name); +} + +Result ovlnrcvRemoveSource(OvlnReceiver *r, const OvlnSourceName *name) { + return serviceDispatchIn(&r->s, 1, *name); +} + +Result ovlnrcvGetReceiveEventHandle(OvlnReceiver *r, Event* out_event) { + Handle tmp_handle = INVALID_HANDLE; + + Result rc = serviceDispatch(&r->s, 2, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &tmp_handle, + ); + if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, true); + return rc; +} + +Result ovlnrcvReceive(OvlnReceiver *r, OvlnRawMessage *message) { + return serviceDispatchOut(&r->s, 3, *message); +} + +Result ovlnrcvReceiveWithTick(OvlnReceiver *r, OvlnRawMessage *message, s64 *tick) { + struct { + OvlnRawMessage message; + s64 tick; + } out; + + Result rc = serviceDispatchOut(&r->s, 4, out); + if (R_SUCCEEDED(rc)) { + *message = out.message; + *tick = out.tick; + } + + return rc; +} + +Result ovlnsndOpenSender(OvlnSender *s, const OvlnSourceName *name, OvlnQueueAttribute attribute) { + const struct { + OvlnSourceName name; + OvlnQueueAttribute attribute; + } in = { *name, attribute }; + + return serviceDispatchIn(&g_ovlnsndSrv, 0, in, + .out_num_objects = 1, + .out_objects = &s->s, + ); +} + +void ovlnsndCloseSender(OvlnSender *s) { + serviceClose(&s->s); +} + +Result ovlnsndSend(OvlnSender *s, OvlnSendOption option, const OvlnRawMessage *message) { + const struct { + OvlnSendOption option; + OvlnRawMessage message; + } in = { option, *message }; + + return serviceDispatchIn(&s->s, 0, in); +} + +Result ovlnsndGetUnreceivedMessageCount(OvlnSender *s, u32 *count) { + return serviceDispatchOut(&s->s, 1, *count); +}