From 7d9601810d70b27b0174adaa04735bd1a7fed148 Mon Sep 17 00:00:00 2001 From: averne <45773016+averne@users.noreply.github.com> Date: Sat, 16 Feb 2019 03:06:41 +0900 Subject: [PATCH] Added nfp:user services wrappers. (#237) * hid: move official/libnx controller converters to hid.h --- nx/include/switch.h | 1 + nx/include/switch/services/hid.h | 12 + nx/include/switch/services/nfc.h | 167 +++++++ nx/source/services/hid.c | 8 +- nx/source/services/nfc.c | 771 +++++++++++++++++++++++++++++++ 5 files changed, 952 insertions(+), 7 deletions(-) create mode 100644 nx/include/switch/services/nfc.h create mode 100644 nx/source/services/nfc.c diff --git a/nx/include/switch.h b/nx/include/switch.h index 857a361f..e2ee6963 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -83,6 +83,7 @@ extern "C" { #include "switch/services/psc.h" #include "switch/services/caps.h" #include "switch/services/capssu.h" +#include "switch/services/nfc.h" #include "switch/display/binder.h" #include "switch/display/parcel.h" diff --git a/nx/include/switch/services/hid.h b/nx/include/switch/services/hid.h index d2bd8e84..56408bec 100644 --- a/nx/include/switch/services/hid.h +++ b/nx/include/switch/services/hid.h @@ -600,6 +600,18 @@ typedef struct HidVibrationValue float freq_high; ///< High Band frequency in Hz. } HidVibrationValue; +static inline u32 hidControllerIDToOfficial(HidControllerID id) { + if (id < CONTROLLER_HANDHELD) return id; + if (id == CONTROLLER_HANDHELD) return 0x20; + return 0x10;//For CONTROLLER_UNKNOWN and invalid values return this. +} + +static inline HidControllerID hidControllerIDFromOfficial(u32 id) { + if (id < 8) return (HidControllerID)id; + if (id == 0x20) return CONTROLLER_HANDHELD; + return CONTROLLER_UNKNOWN; +} + Result hidInitialize(void); void hidExit(void); void hidReset(void); diff --git a/nx/include/switch/services/nfc.h b/nx/include/switch/services/nfc.h new file mode 100644 index 00000000..5642704f --- /dev/null +++ b/nx/include/switch/services/nfc.h @@ -0,0 +1,167 @@ +/** + * @file nfc.h + * @brief Nintendo Figurine (amiibo) Platform (nfp:user) service IPC wrapper. + * @author averne + * @copyright libnx Authors + */ + +#pragma once +#include "../types.h" +#include "../services/hid.h" + +typedef struct { + u8 uuid[10]; + u8 uuid_length; + u8 reserved1[0x15]; + u32 protocol; + u32 tag_type; + u8 reserved2[0x30]; +} PACKED NfpuTagInfo; + +typedef struct { + u16 last_write_year; + u8 last_write_month; + u8 last_write_day; + u16 write_counter; + u16 version; + u32 application_area_size; + u8 reserved[0x34]; +} PACKED NfpuCommonInfo; + +typedef struct { + u8 amiibo_id[0x8]; + u8 reserved[0x38]; +} PACKED NfpuModelInfo; + +typedef struct { + u8 unk_x0[0x10]; // Hash? + u16 mii_name[10+1]; ///< utf-16be, null-terminated + u8 unk_x26; + u8 mii_color; + u8 mii_sex; + u8 mii_height; + u8 mii_width; + u8 unk_x2b[2]; + u8 mii_face_shape; + u8 mii_face_color; + u8 mii_wrinkles_style; + u8 mii_makeup_style; + u8 mii_hair_style; + u8 mii_hair_color; + u8 mii_has_hair_flipped; + u8 mii_eye_style; + u8 mii_eye_color; + u8 mii_eye_size; + u8 mii_eye_thickness; + u8 mii_eye_angle; + u8 mii_eye_pos_x; + u8 mii_eye_pos_y; + u8 mii_eyebrow_style; + u8 mii_eyebrow_color; + u8 mii_eyebrow_size; + u8 mii_eyebrow_thickness; + u8 mii_eyebrow_angle; + u8 mii_eyebrow_pos_x; + u8 mii_eyebrow_pos_y; + u8 mii_nose_style; + u8 mii_nose_size; + u8 mii_nose_pos; + u8 mii_mouth_style; + u8 mii_mouth_color; + u8 mii_mouth_size; + u8 mii_mouth_thickness; + u8 mii_mouth_pos; + u8 mii_facial_hair_color; + u8 mii_beard_style; + u8 mii_mustache_style; + u8 mii_mustache_size; + u8 mii_mustache_pos; + u8 mii_glasses_style; + u8 mii_glasses_color; + u8 mii_glasses_size; + u8 mii_glasses_pos; + u8 mii_has_mole; + u8 mii_mole_size; + u8 mii_mole_pos_x; + u8 mii_mole_pos_y; + u8 unk_x57; +} PACKED NfpuMiiCharInfo; + +typedef struct { + NfpuMiiCharInfo mii; + u16 first_write_year; + u8 first_write_month; + u8 first_write_day; + char amiibo_name[10+1]; ///< utf-8, null-terminated + u8 reserved[0x99]; +} PACKED NfpuRegisterInfo; + +typedef struct { + u64 unk1; + u64 reserved1[3]; + u64 unk2; + u64 reserved2[3]; +} NfpuInitConfig; + +typedef enum { + NfpuState_NonInitialized = 0, + NfpuState_Initialized = 1, +} NfpuState; + +typedef enum { + NfpuDeviceState_Initialized = 0, + NfpuDeviceState_SearchingForTag = 1, + NfpuDeviceState_TagFound = 2, + NfpuDeviceState_TagRemoved = 3, + NfpuDeviceState_TagMounted = 4, + NfpuDeviceState_Unavailable = 5, + NfpuDeviceState_Finalized = 6, +} NfpuDeviceState; + +typedef enum { + NfpuDeviceType_Amiibo = 0, +} NfpuDeviceType; + +typedef enum { + NfpuMountTarget_Rom = 1, + NfpuMountTarget_Ram = 2, + NfpuMountTarget_All = 3, +} NfpuMountTarget; + +Result nfpuInitialize(const NfpuInitConfig *config); +void nfpuExit(void); +Service* nfpuGetInterface(void); + +Result nfpuStartDetection(HidControllerID id); +Result nfpuStopDetection(HidControllerID id); + +/// Returned event will have autoclear off. +Result nfpuAttachActivateEvent(HidControllerID id, Event *out); +/// Returned event will have autoclear off. +Result nfpuAttachDeactivateEvent(HidControllerID id, Event *out); +/// Returned event will have autoclear on. +Result nfpuAttachAvailabilityChangeEvent(Event *out); + +Result nfpuGetState(NfpuState *out); +Result nfpuGetDeviceState(HidControllerID id, NfpuDeviceState *out); +Result nfpuListDevices(u32 *count, HidControllerID *out, size_t num_elements); +Result nfpuGetNpadId(HidControllerID id, u32 *out); + +Result nfpuMount(HidControllerID id, NfpuDeviceType device_type, NfpuMountTarget mount_target); +Result nfpuUnmount(HidControllerID id); + +Result nfpuGetTagInfo(HidControllerID id, NfpuTagInfo *out); +Result nfpuGetRegisterInfo(HidControllerID id, NfpuRegisterInfo *out); +Result nfpuGetCommonInfo(HidControllerID id, NfpuCommonInfo *out); +Result nfpuGetModelInfo(HidControllerID id, NfpuModelInfo *out); + +Result nfpuOpenApplicationArea(HidControllerID id, u32 app_id, u32 *npad_id); +Result nfpuGetApplicationArea(HidControllerID id, void* buf, size_t buf_size); +Result nfpuSetApplicationArea(HidControllerID id, const void* buf, size_t buf_size); +Result nfpuCreateApplicationArea(HidControllerID id, u32 app_id, const void* buf, size_t buf_size); + +Result nfpuFlush(HidControllerID id); +Result nfpuRestore(HidControllerID id); + +/// Calls nfc:user. +Result nfpuIsNfcEnabled(bool *out); diff --git a/nx/source/services/hid.c b/nx/source/services/hid.c index 5d29c925..fadf4a17 100644 --- a/nx/source/services/hid.c +++ b/nx/source/services/hid.c @@ -154,12 +154,6 @@ void hidReset(void) rwlockWriteUnlock(&g_hidLock); } -static u32 _hidControllerIDToOfficial(HidControllerID id) { - if (id < CONTROLLER_HANDHELD) return id; - if (id == CONTROLLER_HANDHELD) return 0x20; - return 0x10;//For CONTROLLER_UNKNOWN and invalid values return this. -} - Service* hidGetSessionService(void) { return &g_hidSrv; } @@ -857,7 +851,7 @@ Result hidAcquireNpadStyleSetUpdateEventHandle(HidControllerID id, Event* event, raw->magic = SFCI_MAGIC; raw->cmd_id = 106; - raw->id = _hidControllerIDToOfficial(id); + raw->id = hidControllerIDToOfficial(id); raw->AppletResourceUserId = AppletResourceUserId; raw->event_ptr = 0;//Official sw sets this to a ptr, which the sysmodule doesn't seem to use. diff --git a/nx/source/services/nfc.c b/nx/source/services/nfc.c new file mode 100644 index 00000000..88d6af50 --- /dev/null +++ b/nx/source/services/nfc.c @@ -0,0 +1,771 @@ +#include +#include "types.h" +#include "arm/atomics.h" +#include "kernel/detect.h" +#include "services/hid.h" +#include "services/applet.h" +#include "services/nfc.h" + +static u64 g_refCnt; +static Service g_nfpuSrv; +static Service g_nfcuSrv; +static Service g_nfpuInterface; +static Service g_nfcuInterface; + +// This is the data passed by every application this was tested with +static const NfpuInitConfig g_nfpuDefaultInitConfig = { + .unk1 = 0x00000001000a0003, + .reserved1 = {0}, + .unk2 = 0x0000000300040003, + .reserved2 = {0}, +}; + +static Result _nfpuCreateInterface(Service* srv, Service* out); +static Result _nfpuInterfaceInitialize(Service* srv, u64 cmd_id, u64 aruid, const NfpuInitConfig *config); +static Result _nfpuInterfaceFinalize(Service* srv, u64 cmd_id); + +static Result _nfpuInterfaceCmdNoInOut(Service* srv, u64 cmd_id); +static Result _nfpuInterfaceCmdInIdNoOut(Service* srv, u64 cmd_id, HidControllerID id); +static Result _nfpuInterfaceCmdInIdOutEvent(Service* srv, u64 cmd_id, HidControllerID id, Event *out); +static Result _nfpuInterfaceCmdInIdOutBuffer(Service* srv, u64 cmd_id, HidControllerID id, void* buf, size_t buf_size); + +Result nfpuInitialize(const NfpuInitConfig *config) { + atomicIncrement64(&g_refCnt); + + if (serviceIsActive(&g_nfpuInterface) && serviceIsActive(&g_nfcuInterface)) + return 0; + + if (config == NULL) + config = &g_nfpuDefaultInitConfig; + + // If this fails (for example because we're a sysmodule) aruid stays zero + u64 aruid = 0; + appletGetAppletResourceUserId(&aruid); + + // nfp:user + Result rc = smGetService(&g_nfpuSrv, "nfp:user"); + + if (R_SUCCEEDED(rc)) + rc = serviceConvertToDomain(&g_nfpuSrv); + + if (R_SUCCEEDED(rc)) + rc = _nfpuCreateInterface(&g_nfpuSrv, &g_nfpuInterface); + + if (R_SUCCEEDED(rc)) + rc = _nfpuInterfaceInitialize(&g_nfpuInterface, 0, aruid, config); + + // nfc:user + if (R_SUCCEEDED(rc)) + rc = smGetService(&g_nfcuSrv, "nfc:user"); + + if (R_SUCCEEDED(rc)) + rc = serviceConvertToDomain(&g_nfcuSrv); + + if (R_SUCCEEDED(rc)) + rc = _nfpuCreateInterface(&g_nfcuSrv, &g_nfcuInterface); + + if (R_SUCCEEDED(rc)) + rc = _nfpuInterfaceInitialize(&g_nfcuInterface, kernelAbove400() ? 0 : 400, aruid, config); + + if (R_FAILED(rc)) + nfpuExit(); + + return rc; +} + +void nfpuExit(void) { + if (atomicDecrement64(&g_refCnt) == 0) { + _nfpuInterfaceFinalize(&g_nfpuInterface, 1); + _nfpuInterfaceFinalize(&g_nfcuInterface, kernelAbove400() ? 1 : 401); + serviceClose(&g_nfpuInterface); + serviceClose(&g_nfcuInterface); + serviceClose(&g_nfpuSrv); + serviceClose(&g_nfcuSrv); + } +} + +Service* nfpuGetInterface(void) { + return &g_nfpuInterface; +} + +static Result _nfpuCreateInterface(Service* srv, Service* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) + serviceCreateSubservice(out, srv, &r, 0); + } + + return rc; +} + +static Result _nfpuInterfaceCmdNoInOut(Service* srv, u64 cmd_id) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _nfpuInterfaceCmdInIdNoOut(Service* srv, u64 cmd_id, HidControllerID id) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->id = hidControllerIDToOfficial(id); + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _nfpuInterfaceCmdInIdOutEvent(Service* srv, u64 cmd_id, HidControllerID id, Event *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->id = hidControllerIDToOfficial(id); + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) + eventLoadRemote(out, r.Handles[0], false); + } + + return rc; +} + +static Result _nfpuInterfaceCmdInIdOutBuffer(Service* srv, u64 cmd_id, HidControllerID id, void* buf, size_t buf_size) { + IpcCommand c; + ipcInitialize(&c); + + ipcAddRecvStatic(&c, buf, buf_size, 0); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->id = hidControllerIDToOfficial(id); + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _nfpuInterfaceInitialize(Service* srv, u64 cmd_id, u64 aruid, const NfpuInitConfig *config) { + IpcCommand c; + ipcInitialize(&c); + + ipcAddSendBuffer(&c, config, sizeof(NfpuInitConfig), BufferType_Normal); + ipcSendPid(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 aruid; + u64 zero; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->aruid = aruid; + raw->zero = 0; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _nfpuInterfaceFinalize(Service* srv, u64 cmd_id) { + return _nfpuInterfaceCmdNoInOut(srv, cmd_id); +} + +Result nfpuStartDetection(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(&g_nfpuInterface, 3, id); +} + +Result nfpuStopDetection(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(&g_nfpuInterface, 4, id); +} + +Result nfpuAttachActivateEvent(HidControllerID id, Event *out) { + return _nfpuInterfaceCmdInIdOutEvent(&g_nfpuInterface, 17, id, out); +} + +Result nfpuAttachDeactivateEvent(HidControllerID id, Event *out) { + return _nfpuInterfaceCmdInIdOutEvent(&g_nfpuInterface, 18, id, out); +} + +Result nfpuAttachAvailabilityChangeEvent(Event *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 23; + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) + eventLoadRemote(out, r.Handles[0], true); + } + + return rc; +} + +Result nfpuGetState(NfpuState *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 19; + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 state; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && out) + *out = resp->state; + } + + return rc; +} + +Result nfpuGetDeviceState(HidControllerID id, NfpuDeviceState *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 20; + raw->id = hidControllerIDToOfficial(id); + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 state; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && out) + *out = resp->state; + } + + return rc; +} + +Result nfpuListDevices(u32 *count, HidControllerID *out, size_t num_elements) { + // This is the maximum number of controllers that can be connected to a console at a time + // Incidentally, this is the biggest value official software (SSBU) was observed using + size_t max_controllers = 9; + if (num_elements > max_controllers) + num_elements = max_controllers; + + IpcCommand c; + ipcInitialize(&c); + + u64 buf[max_controllers]; + memset(buf, 0, max_controllers * sizeof(u64)); + ipcAddRecvStatic(&c, buf, max_controllers * sizeof(u64), 0); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 count; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && count) + *count = resp->count; + + if (R_SUCCEEDED(rc) && out) { + for (size_t i=0; imagic = SFCI_MAGIC; + raw->cmd_id = 21; + raw->id = hidControllerIDToOfficial(id); + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 npad_id; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && out) + *out = resp->npad_id; + } + + return rc; +} + +Result nfpuMount(HidControllerID id, NfpuDeviceType device_type, NfpuMountTarget mount_target) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + u32 device_type; + u32 mount_target; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->id = hidControllerIDToOfficial(id); + raw->device_type = device_type; + raw->mount_target = mount_target; + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result nfpuUnmount(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(&g_nfpuInterface, 6, id); +} + +Result nfpuGetTagInfo(HidControllerID id, NfpuTagInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(&g_nfpuInterface, 13, id, out, sizeof(NfpuTagInfo)); +} + +Result nfpuGetRegisterInfo(HidControllerID id, NfpuRegisterInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(&g_nfpuInterface, 14, id, out, sizeof(NfpuRegisterInfo)); +} + +Result nfpuGetCommonInfo(HidControllerID id, NfpuCommonInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(&g_nfpuInterface, 15, id, out, sizeof(NfpuCommonInfo)); +} + +Result nfpuGetModelInfo(HidControllerID id, NfpuModelInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(&g_nfpuInterface, 16, id, out, sizeof(NfpuModelInfo)); +} + +Result nfpuOpenApplicationArea(HidControllerID id, u32 app_id, u32 *npad_id) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + u32 app_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 7; + raw->id = hidControllerIDToOfficial(id); + raw->app_id = app_id; + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 npad_id; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && npad_id) + *npad_id = resp->npad_id; + } + + return rc; +} + +Result nfpuGetApplicationArea(HidControllerID id, void* buf, size_t buf_size) { + IpcCommand c; + ipcInitialize(&c); + + ipcAddRecvBuffer(&c, buf, buf_size, BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 8; + raw->id = hidControllerIDToOfficial(id); + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result nfpuSetApplicationArea(HidControllerID id, const void* buf, size_t buf_size) { + IpcCommand c; + ipcInitialize(&c); + + ipcAddSendBuffer(&c, buf, buf_size, BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 9; + raw->id = hidControllerIDToOfficial(id); + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result nfpuCreateApplicationArea(HidControllerID id, u32 app_id, const void* buf, size_t buf_size) { + IpcCommand c; + ipcInitialize(&c); + + ipcAddSendBuffer(&c, buf, buf_size, BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + u32 app_id; + } PACKED *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 12; + raw->id = hidControllerIDToOfficial(id); + raw->app_id = app_id; + + Result rc = serviceIpcDispatch(&g_nfpuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_nfpuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result nfpuFlush(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(&g_nfpuInterface, 10, id); +} + +Result nfpuRestore(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(&g_nfpuInterface, 11, id); +} + +Result nfpuIsNfcEnabled(bool *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfcuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = kernelAbove200() ? 3 : 403; + + Result rc = serviceIpcDispatch(&g_nfcuInterface); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u8 flag; + } *resp; + + serviceIpcParse(&g_nfcuInterface, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out) + *out = !!resp->flag; + } + + return rc; +}