From f82d97f88a2abbd61b6bbe9a4903bbe5571370ff Mon Sep 17 00:00:00 2001 From: HookedBehemoth Date: Fri, 4 Dec 2020 21:53:44 +0100 Subject: [PATCH] Add capmtp service wrapper [11.0.0+] (#510) --- nx/include/switch.h | 1 + nx/include/switch/services/capmtp.h | 24 +++++ nx/source/services/capmtp.c | 136 ++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 nx/include/switch/services/capmtp.h create mode 100644 nx/source/services/capmtp.c diff --git a/nx/include/switch.h b/nx/include/switch.h index c2a40424..16b0a838 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -110,6 +110,7 @@ extern "C" { #include "switch/services/capsu.h" #include "switch/services/capssc.h" #include "switch/services/capssu.h" +#include "switch/services/capmtp.h" #include "switch/services/nfc.h" #include "switch/services/wlaninf.h" #include "switch/services/pctl.h" diff --git a/nx/include/switch/services/capmtp.h b/nx/include/switch/services/capmtp.h new file mode 100644 index 00000000..59368666 --- /dev/null +++ b/nx/include/switch/services/capmtp.h @@ -0,0 +1,24 @@ +/** + * @file capmtp.h + * @brief capmtp service IPC wrapper. + * @note Only available on [11.0.0+]. + * @author Behemoth + */ +#pragma once +#include "../types.h" +#include "../kernel/event.h" +#include "../sf/service.h" + +Result capmtpInitialize(void* mem, size_t size, u32 app_count, u32 max_img, u32 max_vid, const char *other_name); +void capmtpExit(void); + +Service* capmtpGetRootServiceSession(void); +Service* capmtpGetServiceSession(void); + +Result capmtpStartCommandHandler(void); +Result capmtpStopCommandHandler(void); +bool capmtpIsRunning(void); +Event *capmtpGetConnectionEvent(void); +bool capmtpIsConnected(void); +Event *capmtpGetScanErrorEvent(void); +Result capmtpGetScanError(void); diff --git a/nx/source/services/capmtp.c b/nx/source/services/capmtp.c new file mode 100644 index 00000000..35e82e7e --- /dev/null +++ b/nx/source/services/capmtp.c @@ -0,0 +1,136 @@ +#include "service_guard.h" +#include "kernel/tmem.h" +#include "runtime/hosversion.h" +#include "runtime/util/utf.h" +#include "services/capmtp.h" +#include "services/sm.h" + +static Service g_capmtpRoot; +static Service g_capmtp; +static TransferMemory g_tmem; +static Event g_connectEvent, g_scanErrorEvent; + +static Result _capmtpOpenSession(Service *srv); +static Result _capmtpOpen(u32 max_folders, u32 max_img, u32 max_vid, const char *other_name); +static Result _capmtpClose(void); +static Result _capmtpNoInEventOut(u32 id, Event* event, bool autoclear); + +NX_GENERATE_SERVICE_GUARD_PARAMS(capmtp, (void* mem, size_t size, u32 max_folders, u32 max_img, u32 max_vid, const char *other_name), (mem, size, max_folders, max_img, max_vid, other_name)); + +Result _capmtpInitialize(void* mem, size_t size, u32 max_folders, u32 max_img, u32 max_vid, const char *other_name) { + Result rc=0; + + if (hosversionBefore(11,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + rc = tmemCreateFromMemory(&g_tmem, mem, size, Perm_None); + + if (R_SUCCEEDED(rc)) rc = smGetService(&g_capmtpRoot, "capmtp"); + if (R_SUCCEEDED(rc)) rc = serviceConvertToDomain(&g_capmtpRoot); + + if (R_SUCCEEDED(rc)) rc = _capmtpOpenSession(&g_capmtp); + + if (R_SUCCEEDED(rc)) rc = _capmtpOpen(max_folders, max_img, max_vid, other_name); + if (R_SUCCEEDED(rc)) rc = _capmtpNoInEventOut(5, &g_connectEvent, false); + if (R_SUCCEEDED(rc)) rc = _capmtpNoInEventOut(7, &g_scanErrorEvent, false); + + return rc; +} + +void _capmtpCleanup(void) { + eventClose(&g_scanErrorEvent); + eventClose(&g_connectEvent); + _capmtpClose(); + serviceClose(&g_capmtp); + serviceClose(&g_capmtpRoot); + tmemClose(&g_tmem); +} + +Service* capmtpGetRootServiceSession(void) { + return &g_capmtpRoot; +} + +Service* capmtpGetServiceSession(void) { + return &g_capmtp; +} + +static Result _capmtpNoIO(u32 id) { + return serviceDispatch(&g_capmtp, id); +} + +static Result _capmtpNoInEventOut(u32 id, Event* event, bool autoclear) { + Result rc=0; + Handle handle=0; + + rc = serviceDispatch(&g_capmtp, id, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &handle, + ); + + eventLoadRemote(event, handle, autoclear); + + return rc; +} + +static Result _capmtpNoInBoolOut(u32 id) { + u8 tmp=0; + return R_SUCCEEDED(serviceDispatchOut(&g_capmtp, id, tmp)) && tmp & 1; +} + +static Result _capmtpOpenSession(Service *srv) { + return serviceDispatch(&g_capmtpRoot, 0, + .out_num_objects = 1, + .out_objects = srv, + ); +} + +static Result _capmtpOpen(u32 max_folders, u32 max_img, u32 max_vid, const char *other_name) { + u16 buffer[0x100]; + size_t len = utf8_to_utf16(buffer, (const u8*)other_name, sizeof(buffer)/sizeof(u16) - 1); + buffer[len] = 0; + const struct { + u32 tmem_size; + u32 folder_count; + u32 max_images; + u32 max_videos; + } in = { (u32)g_tmem.size, max_folders, max_img, max_vid }; + + return serviceDispatchIn(&g_capmtp, 0, in, + .buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias }, + .buffers = { { buffer, 2*(len+1) } }, + .in_num_handles = 1, + .in_handles = { g_tmem.handle }, + ); +} + +static Result _capmtpClose(void) { + return _capmtpNoIO(1); +} + +Result capmtpStartCommandHandler(void) { + return _capmtpNoIO(2); +} + +Result capmtpStopCommandHandler(void) { + return _capmtpNoIO(3); +} + +bool capmtpIsRunning(void) { + return _capmtpNoInBoolOut(4); +} + +Event *capmtpGetConnectionEvent(void) { + return &g_connectEvent; +} + +bool capmtpIsConnected(void) { + return _capmtpNoInBoolOut(6); +} + +Event *capmtpGetScanErrorEvent(void) { + return &g_scanErrorEvent; +} + +Result capmtpGetScanError(void) { + return _capmtpNoIO(8); +}