diff --git a/nx/include/switch.h b/nx/include/switch.h index 857a361f..72ff7996 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/nfpu.h" #include "switch/display/binder.h" #include "switch/display/parcel.h" diff --git a/nx/include/switch/services/nfpu.h b/nx/include/switch/services/nfpu.h new file mode 100644 index 00000000..4fe850cb --- /dev/null +++ b/nx/include/switch/services/nfpu.h @@ -0,0 +1,148 @@ +/** + * @file nfpu.h + * @brief Near Field Proximity (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? + u8 mii_name[2*(10+1)]; ///< utf-16be, null-terminated + u8 unk_x26; + u8 mii_color; + u8 mii_sex; + u8 mii_height; + u8 mii_width; + u8 unk_x1b[2]; + u8 mii_face_shape; + u8 mii_face_color; + u8 mii_wrinkles; + u8 mii_makeup; + 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_x46[5]; // Zero + Mii ID? + char amiibo_name[10+1]; ///< utf-8, null-terminated + u8 reserved[0x99]; +} PACKED NfpuRegisterInfo; + +typedef enum { + NfpuState_NonInitialized = 0, + NfpuState_Initialized = 1, +} NfpuState; + +typedef enum { + NfpuDeviceState_Initialized = 0, + NfpuDeviceState_SearchingForTag = 1, + NfpuDeviceState_TagFound = 2, + NfpuDeviceState_TagRemoved = 3, + NfpuDeviceState_TagNearby = 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; + +typedef struct { + u64 unk1; + u64 reserved1[3]; + u64 unk2; + u64 reserved2[3]; +} NfpuInitConfig; + +const NfpuInitConfig *nfpuGetDefaultInitConfig(void); + +Result nfpuInitialize(void); +void nfpuExit(void); +Service *nfpuGetInterface(void); + +Result nfpuStartDetection(HidControllerID id); +Result nfpuStopDetection(HidControllerID id); + +Result nfpuAttachActivateEvent(HidControllerID id, Event *out); +Result nfpuAttachDeactivateEvent(HidControllerID id, Event *out); +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); diff --git a/nx/source/services/nfpu.c b/nx/source/services/nfpu.c new file mode 100644 index 00000000..9584de9c --- /dev/null +++ b/nx/source/services/nfpu.c @@ -0,0 +1,568 @@ +#include + +#include "types.h" +#include "arm/atomics.h" +#include "services/hid.h" +#include "services/applet.h" +#include "services/nfpu.h" + +static u64 g_refCnt; +static Service g_nfpuSrv; +static Service g_nfpuInterface; + +static Result _nfpuCreateInterface(void); +static Result _nfpuInterfaceInitialize(u64 aruid, const NfpuInitConfig *config); +static Result _nfpuInterfaceFinalize(void); + +static Result _nfpuInterfaceCmdNoInOut(u64 cmd_id); +static Result _nfpuInterfaceCmdInIdNoOut(HidControllerID id, u64 cmd_id); +static Result _nfpuInterfaceCmdInIdOutEvent(HidControllerID id, Event *out, u64 cmd_id); +static Result _nfpuInterfaceCmdInIdOutBuffer(HidControllerID id, void *buf, size_t buf_size, u64 cmd_id); + +static Result _nfpuInterfaceInitialize(u64 aruid, const NfpuInitConfig *config); +static Result _nfpuInterfaceFinalize(void); + + +// 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}, +}; + +const NfpuInitConfig *nfpuGetDefaultInitConfig(void) { + return &g_nfpuDefaultInitConfig; +} + +Result nfpuInitialize(void) { + atomicIncrement64(&g_refCnt); + + if (serviceIsActive(&g_nfpuInterface)) + return 0; + + // If this fails (for example because we're a sysmodule) aruid stays zero + u64 aruid = 0; + appletGetAppletResourceUserId(&aruid); + + Result rc = smGetService(&g_nfpuSrv, "nfp:user"); + + if (R_SUCCEEDED(rc)) + rc = serviceConvertToDomain(&g_nfpuSrv); + + if (R_SUCCEEDED(rc)) + rc = _nfpuCreateInterface(); + + if (R_SUCCEEDED(rc)) + rc = _nfpuInterfaceInitialize(aruid, &g_nfpuDefaultInitConfig); + + if (R_FAILED(rc)) + nfpuExit(); + + return rc; +} + +void nfpuExit(void) { + if (atomicDecrement64(&g_refCnt) == 0) { + _nfpuInterfaceFinalize(); + serviceClose(&g_nfpuInterface); + serviceClose(&g_nfpuSrv); + } +} + +Service *nfpuGetInterface(void) { + return &g_nfpuInterface; +} + +static Result _nfpuCreateInterface(void) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(&g_nfpuSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_nfpuSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) + serviceCreateSubservice(&g_nfpuInterface, &g_nfpuSrv, &r, 0); + } + + return rc; +} + +static u32 _hidControllerIDToOfficial(HidControllerID id) { + if (id < CONTROLLER_HANDHELD) return id; + if (id == CONTROLLER_HANDHELD) return 0x20; + return 0x10; +} + +static HidControllerID _hidOfficialToControllerID(u64 id) { + if (id < 8) return id; + if (id == 0x20) return CONTROLLER_HANDHELD; + return CONTROLLER_UNKNOWN; +} + +static Result _nfpuInterfaceCmdNoInOut(u64 cmd_id) { + 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 = cmd_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; +} + +static Result _nfpuInterfaceCmdInIdNoOut(HidControllerID id, u64 cmd_id) { + 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 = cmd_id; + 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; +} + +static Result _nfpuInterfaceCmdInIdOutEvent(HidControllerID id, Event *out, u64 cmd_id) { + 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 = cmd_id; + 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; + + if (R_SUCCEEDED(rc)) + eventLoadRemote(out, r.Handles[0], true); + } + + return rc; +} + +static Result _nfpuInterfaceCmdInIdOutBuffer(HidControllerID id, void *buf, size_t buf_size, u64 cmd_id) { + IpcCommand c; + ipcInitialize(&c); + + ipcAddRecvStatic(&c, buf, buf_size, 0); + + struct { + u64 magic; + u64 cmd_id; + u64 id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + 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; +} + +static Result _nfpuInterfaceInitialize(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(&g_nfpuInterface, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->aruid = aruid; + raw->zero = 0; + + 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; +} + +static inline Result _nfpuInterfaceFinalize(void) { + return _nfpuInterfaceCmdNoInOut(1); +} + +inline Result nfpuStartDetection(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(id, 3); +} + +inline Result nfpuStopDetection(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(id, 4); +} + +inline Result nfpuAttachActivateEvent(HidControllerID id, Event *out) { + return _nfpuInterfaceCmdInIdOutEvent(id, out, 17); +} + +inline Result nfpuAttachDeactivateEvent(HidControllerID id, Event *out) { + return _nfpuInterfaceCmdInIdOutEvent(id, out, 18); +} + +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], false); + } + + 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) { + IpcCommand c; + ipcInitialize(&c); + + size_t buf_size = num_elements * sizeof(u64); + u64 *buf = malloc(buf_size); + ipcAddRecvStatic(&c, buf, buf_size, 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 && out) { + *count = resp->count; + for (u32 i=0; icount; i++) + out[i] = _hidOfficialToControllerID(buf[i]); + } + } + + free(buf); + return rc; +} + +Result nfpuGetNpadId(HidControllerID id, u32 *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 = 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; +} + +inline Result nfpuUnmount(HidControllerID id) { + return _nfpuInterfaceCmdInIdNoOut(id, 6); +} + +inline Result nfpuGetTagInfo(HidControllerID id, NfpuTagInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(id, out, sizeof(NfpuTagInfo), 13); +} + +inline Result nfpuGetRegisterInfo(HidControllerID id, NfpuRegisterInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(id, out, sizeof(NfpuRegisterInfo), 14); +} + +inline Result nfpuGetCommonInfo(HidControllerID id, NfpuCommonInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(id, out, sizeof(NfpuCommonInfo), 15); +} + +inline Result nfpuGetModelInfo(HidControllerID id, NfpuModelInfo *out) { + return _nfpuInterfaceCmdInIdOutBuffer(id, out, sizeof(NfpuModelInfo), 16); +}