From a3fa64fcc489d2cc1776807a37db3464dcc32686 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 14 Apr 2023 17:46:20 -0600 Subject: service: nfc: Create interface --- src/core/hle/service/nfc/nfc.cpp | 110 ++++++--- src/core/hle/service/nfc/nfc_device.cpp | 1 - src/core/hle/service/nfc/nfc_interface.cpp | 339 +++++++++++++++++++++++++++ src/core/hle/service/nfc/nfc_interface.h | 52 ++++ src/core/hle/service/nfc/nfc_user.cpp | 365 ----------------------------- src/core/hle/service/nfc/nfc_user.h | 52 ---- src/core/hle/service/nfp/nfp.cpp | 24 +- 7 files changed, 466 insertions(+), 477 deletions(-) create mode 100644 src/core/hle/service/nfc/nfc_interface.cpp create mode 100644 src/core/hle/service/nfc/nfc_interface.h delete mode 100644 src/core/hle/service/nfc/nfc_user.cpp delete mode 100644 src/core/hle/service/nfc/nfc_user.h diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 6595e34ed..7a8f59725 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -8,12 +8,83 @@ #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nfc/mifare_user.h" #include "core/hle/service/nfc/nfc.h" -#include "core/hle/service/nfc/nfc_user.h" +#include "core/hle/service/nfc/nfc_interface.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" namespace Service::NFC { +class IUser final : public Interface { +public: + explicit IUser(Core::System& system_) : Interface(system_, "IUser") { + // clang-format off + static const FunctionInfo functions[] = { + {0, &Interface::Initialize, "InitializeOld"}, + {1, &Interface::Finalize, "FinalizeOld"}, + {2, &Interface::GetState, "GetStateOld"}, + {3, &Interface::IsNfcEnabled, "IsNfcEnabledOld"}, + {400, &Interface::Initialize, "Initialize"}, + {401, &Interface::Finalize, "Finalize"}, + {402, &Interface::GetState, "GetState"}, + {403, &Interface::IsNfcEnabled, "IsNfcEnabled"}, + {404, &Interface::ListDevices, "ListDevices"}, + {405, &Interface::GetDeviceState, "GetDeviceState"}, + {406, &Interface::GetNpadId, "GetNpadId"}, + {407, &Interface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {408, &Interface::StartDetection, "StartDetection"}, + {409, &Interface::StopDetection, "StopDetection"}, + {410, &Interface::GetTagInfo, "GetTagInfo"}, + {411, &Interface::AttachActivateEvent, "AttachActivateEvent"}, + {412, &Interface::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {1000, nullptr, "ReadMifare"}, + {1001, nullptr, "WriteMifare"}, + {1300, &Interface::SendCommandByPassThrough, "SendCommandByPassThrough"}, + {1301, nullptr, "KeepPassThroughSession"}, + {1302, nullptr, "ReleasePassThroughSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class ISystem final : public Interface { +public: + explicit ISystem(Core::System& system_) : Interface{system_, "ISystem"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &Interface::Initialize, "InitializeOld"}, + {1, &Interface::Finalize, "FinalizeOld"}, + {2, &Interface::GetState, "GetStateOld"}, + {3, &Interface::IsNfcEnabled, "IsNfcEnabledOld"}, + {100, nullptr, "SetNfcEnabledOld"}, + {400, &Interface::Initialize, "Initialize"}, + {401, &Interface::Finalize, "Finalize"}, + {402, &Interface::GetState, "GetState"}, + {403, &Interface::IsNfcEnabled, "IsNfcEnabled"}, + {404, &Interface::ListDevices, "ListDevices"}, + {405, &Interface::GetDeviceState, "GetDeviceState"}, + {406, &Interface::GetNpadId, "GetNpadId"}, + {407, &Interface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {408, &Interface::StartDetection, "StartDetection"}, + {409, &Interface::StopDetection, "StopDetection"}, + {410, &Interface::GetTagInfo, "GetTagInfo"}, + {411, &Interface::AttachActivateEvent, "AttachActivateEvent"}, + {412, &Interface::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {500, nullptr, "SetNfcEnabled"}, + {510, nullptr, "OutputTestWave"}, + {1000, nullptr, "ReadMifare"}, + {1001, nullptr, "WriteMifare"}, + {1300, &Interface::SendCommandByPassThrough, "SendCommandByPassThrough"}, + {1301, nullptr, "KeepPassThroughSession"}, + {1302, nullptr, "ReleasePassThroughSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + class IAm final : public ServiceFramework { public: explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} { @@ -95,43 +166,6 @@ private: } }; -class ISystem final : public ServiceFramework { -public: - explicit ISystem(Core::System& system_) : ServiceFramework{system_, "ISystem"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "Initialize"}, - {1, nullptr, "Finalize"}, - {2, nullptr, "GetStateOld"}, - {3, nullptr, "IsNfcEnabledOld"}, - {100, nullptr, "SetNfcEnabledOld"}, - {400, nullptr, "InitializeSystem"}, - {401, nullptr, "FinalizeSystem"}, - {402, nullptr, "GetState"}, - {403, nullptr, "IsNfcEnabled"}, - {404, nullptr, "ListDevices"}, - {405, nullptr, "GetDeviceState"}, - {406, nullptr, "GetNpadId"}, - {407, nullptr, "AttachAvailabilityChangeEvent"}, - {408, nullptr, "StartDetection"}, - {409, nullptr, "StopDetection"}, - {410, nullptr, "GetTagInfo"}, - {411, nullptr, "AttachActivateEvent"}, - {412, nullptr, "AttachDeactivateEvent"}, - {500, nullptr, "SetNfcEnabled"}, - {510, nullptr, "OutputTestWave"}, - {1000, nullptr, "ReadMifare"}, - {1001, nullptr, "WriteMifare"}, - {1300, nullptr, "SendCommandByPassThrough"}, - {1301, nullptr, "KeepPassThroughSession"}, - {1302, nullptr, "ReleasePassThroughSession"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - class NFC_SYS final : public ServiceFramework { public: explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} { diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp index c7db74d14..47bc1a05d 100644 --- a/src/core/hle/service/nfc/nfc_device.cpp +++ b/src/core/hle/service/nfc/nfc_device.cpp @@ -11,7 +11,6 @@ #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nfc/nfc_device.h" #include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/nfc/nfc_user.h" namespace Service::NFC { NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp new file mode 100644 index 000000000..be96d0cbc --- /dev/null +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -0,0 +1,339 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/nfc/nfc_device.h" +#include "core/hle/service/nfc/nfc_interface.h" +#include "core/hle/service/nfc/nfc_result.h" +#include "core/hle/service/time/clock_types.h" + +namespace Service::NFC { + +Interface::Interface(Core::System& system_, const char* name) + : ServiceFramework{system_, name}, service_context{system_, service_name} { + availability_change_event = service_context.CreateEvent("Interface:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } +} + +Interface ::~Interface() { + availability_change_event->Close(); +} + +void Interface::Initialize(HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void Interface::Finalize(HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Interface::GetState(HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void Interface::IsNfcEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(state != State::NonInitialized); +} + +void Interface::ListDevices(HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + std::vector nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); + + for (auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.empty()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(nfp_devices.size())); +} + +void Interface::GetDeviceState(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void Interface::GetNpadId(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void Interface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +void Interface::StartDetection(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto nfp_protocol{rp.PopEnum()}; + LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(nfp_protocol); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::StopDetection(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::GetTagInfo(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + NFP::TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info, false); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::AttachActivateEvent(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void Interface::AttachDeactivateEvent(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void Interface::SendCommandByPassThrough(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto timeout{rp.PopRaw()}; + const auto command_data{ctx.ReadBuffer()}; + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", + device_handle, timeout.ToSeconds(), command_data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + std::vector out_data(1); + // TODO: Request data from nfc device + ctx.WriteBuffer(out_data); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(out_data.size())); +} + +std::optional> Interface::GetNfcDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_interface.h b/src/core/hle/service/nfc/nfc_interface.h new file mode 100644 index 000000000..8c1bf5a59 --- /dev/null +++ b/src/core/hle/service/nfc/nfc_interface.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::NFC { +class NfcDevice; + +class Interface : public ServiceFramework { +public: + explicit Interface(Core::System& system_, const char* name); + ~Interface(); + + void Initialize(HLERequestContext& ctx); + void Finalize(HLERequestContext& ctx); + void GetState(HLERequestContext& ctx); + void IsNfcEnabled(HLERequestContext& ctx); + void ListDevices(HLERequestContext& ctx); + void GetDeviceState(HLERequestContext& ctx); + void GetNpadId(HLERequestContext& ctx); + void AttachAvailabilityChangeEvent(HLERequestContext& ctx); + void StartDetection(HLERequestContext& ctx); + void StopDetection(HLERequestContext& ctx); + void GetTagInfo(HLERequestContext& ctx); + void AttachActivateEvent(HLERequestContext& ctx); + void AttachDeactivateEvent(HLERequestContext& ctx); + void SendCommandByPassThrough(HLERequestContext& ctx); + +private: + enum class State : u32 { + NonInitialized, + Initialized, + }; + + std::optional> GetNfcDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp deleted file mode 100644 index 7c162a4f3..000000000 --- a/src/core/hle/service/nfc/nfc_user.cpp +++ /dev/null @@ -1,365 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hid/hid_types.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfc/nfc_device.h" -#include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/nfc/nfc_user.h" -#include "core/hle/service/time/clock_types.h" - -namespace Service::NFC { - -IUser::IUser(Core::System& system_) - : ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} { - static const FunctionInfo functions[] = { - {0, &IUser::Initialize, "InitializeOld"}, - {1, &IUser::Finalize, "FinalizeOld"}, - {2, &IUser::GetState, "GetStateOld"}, - {3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"}, - {400, &IUser::Initialize, "Initialize"}, - {401, &IUser::Finalize, "Finalize"}, - {402, &IUser::GetState, "GetState"}, - {403, &IUser::IsNfcEnabled, "IsNfcEnabled"}, - {404, &IUser::ListDevices, "ListDevices"}, - {405, &IUser::GetDeviceState, "GetDeviceState"}, - {406, &IUser::GetNpadId, "GetNpadId"}, - {407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, - {408, &IUser::StartDetection, "StartDetection"}, - {409, &IUser::StopDetection, "StopDetection"}, - {410, &IUser::GetTagInfo, "GetTagInfo"}, - {411, &IUser::AttachActivateEvent, "AttachActivateEvent"}, - {412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, - {1000, nullptr, "ReadMifare"}, - {1001, nullptr, "WriteMifare"}, - {1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"}, - {1301, nullptr, "KeepPassThroughSession"}, - {1302, nullptr, "ReleasePassThroughSession"}, - }; - RegisterHandlers(functions); - - availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); - - for (u32 device_index = 0; device_index < 10; device_index++) { - devices[device_index] = - std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, - service_context, availability_change_event); - } -} - -IUser ::~IUser() { - availability_change_event->Close(); -} - -void IUser::Initialize(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::Initialized; - - for (auto& device : devices) { - device->Initialize(); - } - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); -} - -void IUser::Finalize(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::NonInitialized; - - for (auto& device : devices) { - device->Finalize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IUser::GetState(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(state); -} - -void IUser::IsNfcEnabled(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(state != State::NonInitialized); -} - -void IUser::ListDevices(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - if (!ctx.CanWriteBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - if (ctx.GetWriteBufferSize() == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - std::vector nfp_devices; - const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); - - for (auto& device : devices) { - if (nfp_devices.size() >= max_allowed_devices) { - continue; - } - if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { - nfp_devices.push_back(device->GetHandle()); - } - } - - if (nfp_devices.empty()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - ctx.WriteBuffer(nfp_devices); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(nfp_devices.size())); -} - -void IUser::GetDeviceState(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetCurrentState()); -} - -void IUser::GetNpadId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetNpadId()); -} - -void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); -} - -void IUser::StartDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto nfp_protocol{rp.PopEnum()}; - LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->StartDetection(nfp_protocol); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void IUser::StopDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->StopDetection(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void IUser::GetTagInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - NFP::TagInfo tag_info{}; - const auto result = device.value()->GetTagInfo(tag_info, false); - ctx.WriteBuffer(tag_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void IUser::AttachActivateEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetActivateEvent()); -} - -void IUser::AttachDeactivateEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetDeactivateEvent()); -} - -void IUser::SendCommandByPassThrough(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto timeout{rp.PopRaw()}; - const auto command_data{ctx.ReadBuffer()}; - - LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", - device_handle, timeout.ToSeconds(), command_data.size()); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - std::vector out_data(1); - // TODO: Request data from nfc device - ctx.WriteBuffer(out_data); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(out_data.size())); -} - -std::optional> IUser::GetNfcDevice(u64 handle) { - for (auto& device : devices) { - if (device->GetHandle() == handle) { - return device; - } - } - return std::nullopt; -} - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_user.h b/src/core/hle/service/nfc/nfc_user.h deleted file mode 100644 index aee046ae8..000000000 --- a/src/core/hle/service/nfc/nfc_user.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Service::NFC { -class NfcDevice; - -class IUser final : public ServiceFramework { -public: - explicit IUser(Core::System& system_); - ~IUser(); - -private: - enum class State : u32 { - NonInitialized, - Initialized, - }; - - void Initialize(HLERequestContext& ctx); - void Finalize(HLERequestContext& ctx); - void GetState(HLERequestContext& ctx); - void IsNfcEnabled(HLERequestContext& ctx); - void ListDevices(HLERequestContext& ctx); - void GetDeviceState(HLERequestContext& ctx); - void GetNpadId(HLERequestContext& ctx); - void AttachAvailabilityChangeEvent(HLERequestContext& ctx); - void StartDetection(HLERequestContext& ctx); - void StopDetection(HLERequestContext& ctx); - void GetTagInfo(HLERequestContext& ctx); - void AttachActivateEvent(HLERequestContext& ctx); - void AttachDeactivateEvent(HLERequestContext& ctx); - void SendCommandByPassThrough(HLERequestContext& ctx); - - std::optional> GetNfcDevice(u64 handle); - - KernelHelpers::ServiceContext service_context; - - std::array, 10> devices{}; - - State state{State::NonInitialized}; - Kernel::KEvent* availability_change_event; -}; - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 2714f4bea..2559fe598 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -152,16 +152,10 @@ private: void CreateUserInterface(HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); - if (user_interface == nullptr) { - user_interface = std::make_shared(system); - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(user_interface); + rb.PushIpcInterface(system); } - - std::shared_ptr user_interface; }; class ISystemManager final : public ServiceFramework { @@ -180,16 +174,10 @@ private: void CreateSystemInterface(HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); - if (system_interface == nullptr) { - system_interface = std::make_shared(system); - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system_interface); + rb.PushIpcInterface(system); } - - std::shared_ptr system_interface; }; class IDebugManager final : public ServiceFramework { @@ -208,16 +196,10 @@ private: void CreateDebugInterface(HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); - if (system_interface == nullptr) { - system_interface = std::make_shared(system); - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system_interface); + rb.PushIpcInterface(system); } - - std::shared_ptr system_interface; }; void LoopProcess(Core::System& system) { -- cgit v1.2.3 From 00d76fc5f5276b74f4c14edda579981831808d35 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 14 Apr 2023 17:55:13 -0600 Subject: service: nfc: Create mifare interface --- src/core/hle/service/nfc/mifare_interface.cpp | 382 ++++++++++++++++++++++++ src/core/hle/service/nfc/mifare_interface.h | 52 ++++ src/core/hle/service/nfc/mifare_user.cpp | 400 -------------------------- src/core/hle/service/nfc/mifare_user.h | 52 ---- src/core/hle/service/nfc/nfc.cpp | 32 ++- 5 files changed, 463 insertions(+), 455 deletions(-) create mode 100644 src/core/hle/service/nfc/mifare_interface.cpp create mode 100644 src/core/hle/service/nfc/mifare_interface.h delete mode 100644 src/core/hle/service/nfc/mifare_user.cpp delete mode 100644 src/core/hle/service/nfc/mifare_user.h diff --git a/src/core/hle/service/nfc/mifare_interface.cpp b/src/core/hle/service/nfc/mifare_interface.cpp new file mode 100644 index 000000000..7e6635ba2 --- /dev/null +++ b/src/core/hle/service/nfc/mifare_interface.cpp @@ -0,0 +1,382 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/nfc/mifare_interface.h" +#include "core/hle/service/nfc/nfc_device.h" +#include "core/hle/service/nfc/nfc_result.h" + +namespace Service::NFC { + +MFInterface::MFInterface(Core::System& system_, const char* name) + : ServiceFramework{system_, name}, service_context{system_, service_name} { + availability_change_event = service_context.CreateEvent("MFInterface:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } +} + +MFInterface ::~MFInterface() { + availability_change_event->Close(); +} + +void MFInterface::Initialize(HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void MFInterface::Finalize(HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void MFInterface::ListDevices(HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareInvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareInvalidArgument); + return; + } + + std::vector nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); + + for (const auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.empty()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(nfp_devices.size())); +} + +void MFInterface::StartDetection(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(NFP::TagProtocol::All); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFInterface::StopDetection(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFInterface::Read(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements()}; + std::vector read_commands(number_of_commands); + + memcpy(read_commands.data(), buffer.data(), + number_of_commands * sizeof(NFP::MifareReadBlockParameter)); + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", + device_handle, number_of_commands); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + Result result = ResultSuccess; + std::vector out_data(number_of_commands); + for (std::size_t i = 0; i < number_of_commands; i++) { + result = device.value()->MifareRead(read_commands[i], out_data[i]); + if (result.IsError()) { + break; + } + } + + ctx.WriteBuffer(out_data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFInterface::Write(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements()}; + std::vector write_commands(number_of_commands); + + memcpy(write_commands.data(), buffer.data(), + number_of_commands * sizeof(NFP::MifareWriteBlockParameter)); + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}", + device_handle, number_of_commands); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + Result result = ResultSuccess; + std::vector out_data(number_of_commands); + for (std::size_t i = 0; i < number_of_commands; i++) { + result = device.value()->MifareWrite(write_commands[i]); + if (result.IsError()) { + break; + } + } + + if (result.IsSuccess()) { + result = device.value()->Flush(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFInterface::GetTagInfo(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + NFP::TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info, true); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFInterface::GetActivateEventHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void MFInterface::GetDeactivateEventHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void MFInterface::GetState(HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void MFInterface::GetDeviceState(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void MFInterface::GetNpadId(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void MFInterface::GetAvailabilityChangeEventHandle(HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +std::optional> MFInterface::GetNfcDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_interface.h b/src/core/hle/service/nfc/mifare_interface.h new file mode 100644 index 000000000..698c8a6b6 --- /dev/null +++ b/src/core/hle/service/nfc/mifare_interface.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::NFC { +class NfcDevice; + +class MFInterface : public ServiceFramework { +public: + explicit MFInterface(Core::System& system_, const char* name); + ~MFInterface(); + + void Initialize(HLERequestContext& ctx); + void Finalize(HLERequestContext& ctx); + void ListDevices(HLERequestContext& ctx); + void StartDetection(HLERequestContext& ctx); + void StopDetection(HLERequestContext& ctx); + void Read(HLERequestContext& ctx); + void Write(HLERequestContext& ctx); + void GetTagInfo(HLERequestContext& ctx); + void GetActivateEventHandle(HLERequestContext& ctx); + void GetDeactivateEventHandle(HLERequestContext& ctx); + void GetState(HLERequestContext& ctx); + void GetDeviceState(HLERequestContext& ctx); + void GetNpadId(HLERequestContext& ctx); + void GetAvailabilityChangeEventHandle(HLERequestContext& ctx); + +private: + enum class State : u32 { + NonInitialized, + Initialized, + }; + + std::optional> GetNfcDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_user.cpp b/src/core/hle/service/nfc/mifare_user.cpp deleted file mode 100644 index e0bbd46e1..000000000 --- a/src/core/hle/service/nfc/mifare_user.cpp +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hid/hid_types.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfc/mifare_user.h" -#include "core/hle/service/nfc/nfc_device.h" -#include "core/hle/service/nfc/nfc_result.h" - -namespace Service::NFC { - -MFIUser::MFIUser(Core::System& system_) - : ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} { - static const FunctionInfo functions[] = { - {0, &MFIUser::Initialize, "Initialize"}, - {1, &MFIUser::Finalize, "Finalize"}, - {2, &MFIUser::ListDevices, "ListDevices"}, - {3, &MFIUser::StartDetection, "StartDetection"}, - {4, &MFIUser::StopDetection, "StopDetection"}, - {5, &MFIUser::Read, "Read"}, - {6, &MFIUser::Write, "Write"}, - {7, &MFIUser::GetTagInfo, "GetTagInfo"}, - {8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"}, - {9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"}, - {10, &MFIUser::GetState, "GetState"}, - {11, &MFIUser::GetDeviceState, "GetDeviceState"}, - {12, &MFIUser::GetNpadId, "GetNpadId"}, - {13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"}, - }; - RegisterHandlers(functions); - - availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent"); - - for (u32 device_index = 0; device_index < 10; device_index++) { - devices[device_index] = - std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, - service_context, availability_change_event); - } -} - -MFIUser ::~MFIUser() { - availability_change_event->Close(); -} - -void MFIUser::Initialize(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::Initialized; - - for (auto& device : devices) { - device->Initialize(); - } - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); -} - -void MFIUser::Finalize(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::NonInitialized; - - for (auto& device : devices) { - device->Finalize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void MFIUser::ListDevices(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - if (!ctx.CanWriteBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareInvalidArgument); - return; - } - - if (ctx.GetWriteBufferSize() == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareInvalidArgument); - return; - } - - std::vector nfp_devices; - const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); - - for (const auto& device : devices) { - if (nfp_devices.size() >= max_allowed_devices) { - continue; - } - if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { - nfp_devices.push_back(device->GetHandle()); - } - } - - if (nfp_devices.empty()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - ctx.WriteBuffer(nfp_devices); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(nfp_devices.size())); -} - -void MFIUser::StartDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - const auto result = device.value()->StartDetection(NFP::TagProtocol::All); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFIUser::StopDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - const auto result = device.value()->StopDetection(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFIUser::Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto buffer{ctx.ReadBuffer()}; - const auto number_of_commands{ctx.GetReadBufferNumElements()}; - std::vector read_commands(number_of_commands); - - memcpy(read_commands.data(), buffer.data(), - number_of_commands * sizeof(NFP::MifareReadBlockParameter)); - - LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", - device_handle, number_of_commands); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - Result result = ResultSuccess; - std::vector out_data(number_of_commands); - for (std::size_t i = 0; i < number_of_commands; i++) { - result = device.value()->MifareRead(read_commands[i], out_data[i]); - if (result.IsError()) { - break; - } - } - - ctx.WriteBuffer(out_data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFIUser::Write(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto buffer{ctx.ReadBuffer()}; - const auto number_of_commands{ctx.GetReadBufferNumElements()}; - std::vector write_commands(number_of_commands); - - memcpy(write_commands.data(), buffer.data(), - number_of_commands * sizeof(NFP::MifareWriteBlockParameter)); - - LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}", - device_handle, number_of_commands); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - Result result = ResultSuccess; - std::vector out_data(number_of_commands); - for (std::size_t i = 0; i < number_of_commands; i++) { - result = device.value()->MifareWrite(write_commands[i]); - if (result.IsError()) { - break; - } - } - - if (result.IsSuccess()) { - result = device.value()->Flush(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFIUser::GetTagInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - NFP::TagInfo tag_info{}; - const auto result = device.value()->GetTagInfo(tag_info, true); - ctx.WriteBuffer(tag_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFIUser::GetActivateEventHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetActivateEvent()); -} - -void MFIUser::GetDeactivateEventHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetDeactivateEvent()); -} - -void MFIUser::GetState(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(state); -} - -void MFIUser::GetDeviceState(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetCurrentState()); -} - -void MFIUser::GetNpadId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetNpadId()); -} - -void MFIUser::GetAvailabilityChangeEventHandle(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); -} - -std::optional> MFIUser::GetNfcDevice(u64 handle) { - for (auto& device : devices) { - if (device->GetHandle() == handle) { - return device; - } - } - return std::nullopt; -} - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_user.h b/src/core/hle/service/nfc/mifare_user.h deleted file mode 100644 index 9701f1d7f..000000000 --- a/src/core/hle/service/nfc/mifare_user.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Service::NFC { -class NfcDevice; - -class MFIUser final : public ServiceFramework { -public: - explicit MFIUser(Core::System& system_); - ~MFIUser(); - -private: - enum class State : u32 { - NonInitialized, - Initialized, - }; - - void Initialize(HLERequestContext& ctx); - void Finalize(HLERequestContext& ctx); - void ListDevices(HLERequestContext& ctx); - void StartDetection(HLERequestContext& ctx); - void StopDetection(HLERequestContext& ctx); - void Read(HLERequestContext& ctx); - void Write(HLERequestContext& ctx); - void GetTagInfo(HLERequestContext& ctx); - void GetActivateEventHandle(HLERequestContext& ctx); - void GetDeactivateEventHandle(HLERequestContext& ctx); - void GetState(HLERequestContext& ctx); - void GetDeviceState(HLERequestContext& ctx); - void GetNpadId(HLERequestContext& ctx); - void GetAvailabilityChangeEventHandle(HLERequestContext& ctx); - - std::optional> GetNfcDevice(u64 handle); - - KernelHelpers::ServiceContext service_context; - - std::array, 10> devices{}; - - State state{State::NonInitialized}; - Kernel::KEvent* availability_change_event; -}; - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 7a8f59725..444d65f07 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -6,7 +6,7 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfc/mifare_user.h" +#include "core/hle/service/nfc/mifare_interface.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc_interface.h" #include "core/hle/service/server_manager.h" @@ -16,7 +16,7 @@ namespace Service::NFC { class IUser final : public Interface { public: - explicit IUser(Core::System& system_) : Interface(system_, "IUser") { + explicit IUser(Core::System& system_) : Interface(system_, "NFC::IUser") { // clang-format off static const FunctionInfo functions[] = { {0, &Interface::Initialize, "InitializeOld"}, @@ -50,7 +50,7 @@ public: class ISystem final : public Interface { public: - explicit ISystem(Core::System& system_) : Interface{system_, "ISystem"} { + explicit ISystem(Core::System& system_) : Interface{system_, "NFC::ISystem"} { // clang-format off static const FunctionInfo functions[] = { {0, &Interface::Initialize, "InitializeOld"}, @@ -85,6 +85,32 @@ public: } }; +class MFIUser final : public MFInterface { +public: + explicit MFIUser(Core::System& system_) : MFInterface{system_, "NFC::MFInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &MFIUser::Initialize, "Initialize"}, + {1, &MFIUser::Finalize, "Finalize"}, + {2, &MFIUser::ListDevices, "ListDevices"}, + {3, &MFIUser::StartDetection, "StartDetection"}, + {4, &MFIUser::StopDetection, "StopDetection"}, + {5, &MFIUser::Read, "Read"}, + {6, &MFIUser::Write, "Write"}, + {7, &MFIUser::GetTagInfo, "GetTagInfo"}, + {8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"}, + {9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"}, + {10, &MFIUser::GetState, "GetState"}, + {11, &MFIUser::GetDeviceState, "GetDeviceState"}, + {12, &MFIUser::GetNpadId, "GetNpadId"}, + {13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + class IAm final : public ServiceFramework { public: explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} { -- cgit v1.2.3 From 5e16fe4579e10cf21af9fada603d8d585f683caf Mon Sep 17 00:00:00 2001 From: german77 Date: Tue, 25 Apr 2023 23:33:05 -0600 Subject: core: service: Add FunctionInfoTyped to allow expanding existing interfaces --- src/core/hle/service/service.h | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 0f79a1b7e..45b2c43b7 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -142,7 +142,8 @@ template class ServiceFramework : public ServiceFrameworkBase { protected: /// Contains information about a request type which is handled by the service. - struct FunctionInfo : FunctionInfoBase { + template + struct FunctionInfoTyped : FunctionInfoBase { // TODO(yuriks): This function could be constexpr, but clang is the only compiler that // doesn't emit an ICE or a wrong diagnostic because of the static_cast. @@ -155,12 +156,13 @@ protected: * the request * @param name_ human-friendly name for the request. Used mostly for logging purposes. */ - FunctionInfo(u32 expected_header_, HandlerFnP handler_callback_, const char* name_) + FunctionInfoTyped(u32 expected_header_, HandlerFnP handler_callback_, const char* name_) : FunctionInfoBase{ expected_header_, // Type-erase member function pointer by casting it down to the base class. static_cast>(handler_callback_), name_} {} }; + using FunctionInfo = FunctionInfoTyped; /** * Initializes the handler with no functions installed. @@ -175,8 +177,8 @@ protected: : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} /// Registers handlers in the service. - template - void RegisterHandlers(const FunctionInfo (&functions)[N]) { + template + void RegisterHandlers(const FunctionInfoTyped (&functions)[N]) { RegisterHandlers(functions, N); } @@ -184,13 +186,14 @@ protected: * Registers handlers in the service. Usually prefer using the other RegisterHandlers * overload in order to avoid needing to specify the array size. */ - void RegisterHandlers(const FunctionInfo* functions, std::size_t n) { + template + void RegisterHandlers(const FunctionInfoTyped* functions, std::size_t n) { RegisterHandlersBase(functions, n); } /// Registers handlers in the service. - template - void RegisterHandlersTipc(const FunctionInfo (&functions)[N]) { + template + void RegisterHandlersTipc(const FunctionInfoTyped (&functions)[N]) { RegisterHandlersTipc(functions, N); } @@ -198,7 +201,8 @@ protected: * Registers handlers in the service. Usually prefer using the other RegisterHandlers * overload in order to avoid needing to specify the array size. */ - void RegisterHandlersTipc(const FunctionInfo* functions, std::size_t n) { + template + void RegisterHandlersTipc(const FunctionInfoTyped* functions, std::size_t n) { RegisterHandlersBaseTipc(functions, n); } -- cgit v1.2.3 From 94151097b9abadf35c55ea06a31925c9848f4c62 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Wed, 19 Apr 2023 19:01:23 -0600 Subject: service: nfc: Merge device interfaces and create the device manager --- src/core/CMakeLists.txt | 21 +- src/core/frontend/applets/cabinet.cpp | 2 +- src/core/frontend/applets/cabinet.h | 10 +- src/core/hle/service/am/applets/applet_cabinet.cpp | 24 +- src/core/hle/service/am/applets/applet_cabinet.h | 6 +- src/core/hle/service/nfc/common/amiibo_crypto.cpp | 405 +++++++ src/core/hle/service/nfc/common/amiibo_crypto.h | 106 ++ src/core/hle/service/nfc/common/device.cpp | 1249 ++++++++++++++++++++ src/core/hle/service/nfc/common/device.h | 138 +++ src/core/hle/service/nfc/common/device_manager.cpp | 695 +++++++++++ src/core/hle/service/nfc/common/device_manager.h | 100 ++ src/core/hle/service/nfc/mifare_interface.cpp | 382 ------ src/core/hle/service/nfc/mifare_interface.h | 52 - src/core/hle/service/nfc/mifare_result.h | 17 + src/core/hle/service/nfc/mifare_types.h | 63 + src/core/hle/service/nfc/nfc.cpp | 125 +- src/core/hle/service/nfc/nfc_device.cpp | 287 ----- src/core/hle/service/nfc/nfc_device.h | 78 -- src/core/hle/service/nfc/nfc_interface.cpp | 399 ++++--- src/core/hle/service/nfc/nfc_interface.h | 33 +- src/core/hle/service/nfc/nfc_result.h | 33 +- src/core/hle/service/nfc/nfc_types.h | 90 ++ src/core/hle/service/nfp/amiibo_crypto.cpp | 405 ------- src/core/hle/service/nfp/amiibo_crypto.h | 106 -- src/core/hle/service/nfp/nfp.cpp | 12 +- src/core/hle/service/nfp/nfp_device.cpp | 1137 ------------------ src/core/hle/service/nfp/nfp_device.h | 120 -- src/core/hle/service/nfp/nfp_interface.cpp | 839 ++----------- src/core/hle/service/nfp/nfp_interface.h | 37 +- src/core/hle/service/nfp/nfp_result.h | 29 +- src/core/hle/service/nfp/nfp_types.h | 129 +- src/yuzu/applets/qt_amiibo_settings.cpp | 12 +- src/yuzu/applets/qt_amiibo_settings.h | 14 +- src/yuzu/main.cpp | 6 +- src/yuzu/main.h | 8 +- 35 files changed, 3395 insertions(+), 3774 deletions(-) create mode 100644 src/core/hle/service/nfc/common/amiibo_crypto.cpp create mode 100644 src/core/hle/service/nfc/common/amiibo_crypto.h create mode 100644 src/core/hle/service/nfc/common/device.cpp create mode 100644 src/core/hle/service/nfc/common/device.h create mode 100644 src/core/hle/service/nfc/common/device_manager.cpp create mode 100644 src/core/hle/service/nfc/common/device_manager.h delete mode 100644 src/core/hle/service/nfc/mifare_interface.cpp delete mode 100644 src/core/hle/service/nfc/mifare_interface.h create mode 100644 src/core/hle/service/nfc/mifare_result.h create mode 100644 src/core/hle/service/nfc/mifare_types.h delete mode 100644 src/core/hle/service/nfc/nfc_device.cpp delete mode 100644 src/core/hle/service/nfc/nfc_device.h create mode 100644 src/core/hle/service/nfc/nfc_types.h delete mode 100644 src/core/hle/service/nfp/amiibo_crypto.cpp delete mode 100644 src/core/hle/service/nfp/amiibo_crypto.h delete mode 100644 src/core/hle/service/nfp/nfp_device.cpp delete mode 100644 src/core/hle/service/nfp/nfp_device.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8817a99c9..45328158f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -555,21 +555,22 @@ add_library(core STATIC hle/service/mnpp/mnpp_app.h hle/service/ncm/ncm.cpp hle/service/ncm/ncm.h - hle/service/nfc/mifare_user.cpp - hle/service/nfc/mifare_user.h + hle/service/nfc/common/amiibo_crypto.cpp + hle/service/nfc/common/amiibo_crypto.h + hle/service/nfc/common/device.cpp + hle/service/nfc/common/device.h + hle/service/nfc/common/device_manager.cpp + hle/service/nfc/common/device_manager.h + hle/service/nfc/mifare_result.h + hle/service/nfc/mifare_types.h hle/service/nfc/nfc.cpp hle/service/nfc/nfc.h - hle/service/nfc/nfc_device.cpp - hle/service/nfc/nfc_device.h + hle/service/nfc/nfc_interface.cpp + hle/service/nfc/nfc_interface.h hle/service/nfc/nfc_result.h - hle/service/nfc/nfc_user.cpp - hle/service/nfc/nfc_user.h - hle/service/nfp/amiibo_crypto.cpp - hle/service/nfp/amiibo_crypto.h + hle/service/nfc/nfc_types.h hle/service/nfp/nfp.cpp hle/service/nfp/nfp.h - hle/service/nfp/nfp_device.cpp - hle/service/nfp/nfp_device.h hle/service/nfp/nfp_interface.cpp hle/service/nfp/nfp_interface.h hle/service/nfp/nfp_result.h diff --git a/src/core/frontend/applets/cabinet.cpp b/src/core/frontend/applets/cabinet.cpp index 2d501eeae..c33ce248b 100644 --- a/src/core/frontend/applets/cabinet.cpp +++ b/src/core/frontend/applets/cabinet.cpp @@ -14,7 +14,7 @@ void DefaultCabinetApplet::Close() const {} void DefaultCabinetApplet::ShowCabinetApplet( const CabinetCallback& callback, const CabinetParameters& parameters, - std::shared_ptr nfp_device) const { + std::shared_ptr nfp_device) const { LOG_WARNING(Service_AM, "(STUBBED) called"); callback(false, {}); } diff --git a/src/core/frontend/applets/cabinet.h b/src/core/frontend/applets/cabinet.h index 74dc5a4f6..af3fc6c3d 100644 --- a/src/core/frontend/applets/cabinet.h +++ b/src/core/frontend/applets/cabinet.h @@ -7,9 +7,9 @@ #include "core/frontend/applets/applet.h" #include "core/hle/service/nfp/nfp_types.h" -namespace Service::NFP { -class NfpDevice; -} // namespace Service::NFP +namespace Service::NFC { +class NfcDevice; +} // namespace Service::NFC namespace Core::Frontend { @@ -26,14 +26,14 @@ public: virtual ~CabinetApplet(); virtual void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters, - std::shared_ptr nfp_device) const = 0; + std::shared_ptr nfp_device) const = 0; }; class DefaultCabinetApplet final : public CabinetApplet { public: void Close() const override; void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters, - std::shared_ptr nfp_device) const override; + std::shared_ptr nfp_device) const override; }; } // namespace Core::Frontend diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 93c9f2a55..8b754e9d4 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -11,7 +11,7 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applet_cabinet.h" #include "core/hle/service/mii/mii_manager.h" -#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfc/common/device.h" namespace Service::AM::Applets { @@ -72,10 +72,10 @@ void Cabinet::Execute() { // TODO: listen on all controllers if (nfp_device == nullptr) { - nfp_device = std::make_shared( + nfp_device = std::make_shared( system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event); nfp_device->Initialize(); - nfp_device->StartDetection(Service::NFP::TagProtocol::All); + nfp_device->StartDetection(Service::NFC::NfcProtocol::All); } const Core::Frontend::CabinetParameters parameters{ @@ -106,20 +106,22 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) Cancel(); } - if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && - nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { + if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound && + nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) { Cancel(); } - if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) { - nfp_device->Mount(Service::NFP::MountTarget::All); + if (nfp_device->GetCurrentState() == Service::NFC::DeviceState::TagFound) { + nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All); } switch (applet_input_common.applet_mode) { case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: { - Service::NFP::AmiiboName name{}; - std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1)); - nfp_device->SetRegisterInfoPrivate(name); + Service::NFP::RegisterInfoPrivate register_info{}; + std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), + std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); + + nfp_device->SetRegisterInfoPrivate(register_info); break; } case Service::NFP::CabinetMode::StartGameDataEraser: @@ -139,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) applet_output.device_handle = applet_input_common.device_handle; applet_output.result = CabinetResult::Cancel; const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); - const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info); + const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false); nfp_device->Finalize(); if (reg_result.IsSuccess()) { diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/applets/applet_cabinet.h index edd295a27..b56427021 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.h +++ b/src/core/hle/service/am/applets/applet_cabinet.h @@ -19,8 +19,8 @@ namespace Core { class System; } // namespace Core -namespace Service::NFP { -class NfpDevice; +namespace Service::NFC { +class NfcDevice; } namespace Service::AM::Applets { @@ -96,7 +96,7 @@ private: Core::System& system; bool is_complete{false}; - std::shared_ptr nfp_device; + std::shared_ptr nfp_device; Kernel::KEvent* availability_change_event; KernelHelpers::ServiceContext service_context; StartParamForAmiiboSettings applet_input_common{}; diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp new file mode 100644 index 000000000..f3901ee8d --- /dev/null +++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp @@ -0,0 +1,405 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2017 socram8888/amiitool +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "core/hle/service/nfc/common/amiibo_crypto.h" + +namespace Service::NFP::AmiiboCrypto { + +bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { + const auto& amiibo_data = ntag_file.user_memory; + LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); + LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); + LOG_DEBUG(Service_NFP, "write_count={}", static_cast(amiibo_data.write_counter)); + + LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); + LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); + LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); + LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", + static_cast(amiibo_data.model_info.model_number)); + LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); + LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); + + LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); + LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); + LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", ntag_file.CFG1); + + // Validate UUID + constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` + if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != + ntag_file.uuid.uid[3]) { + return false; + } + if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ + ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { + return false; + } + + // Check against all know constants on an amiibo binary + if (ntag_file.static_lock != 0xE00F) { + return false; + } + if (ntag_file.compability_container != 0xEEFF10F1U) { + return false; + } + if (amiibo_data.constant_value != 0xA5) { + return false; + } + if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) { + return false; + } + if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { + return false; + } + if (ntag_file.CFG0 != 0x04000000U) { + return false; + } + if (ntag_file.CFG1 != 0x5F) { + return false; + } + return true; +} + +bool IsAmiiboValid(const NTAG215File& ntag_file) { + return IsAmiiboValid(EncodedDataToNfcData(ntag_file)); +} + +NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { + NTAG215File encoded_data{}; + + encoded_data.uid = nfc_data.uuid.uid; + encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; + encoded_data.static_lock = nfc_data.static_lock; + encoded_data.compability_container = nfc_data.compability_container; + encoded_data.hmac_data = nfc_data.user_memory.hmac_data; + encoded_data.constant_value = nfc_data.user_memory.constant_value; + encoded_data.write_counter = nfc_data.user_memory.write_counter; + encoded_data.amiibo_version = nfc_data.user_memory.amiibo_version; + encoded_data.settings = nfc_data.user_memory.settings; + encoded_data.owner_mii = nfc_data.user_memory.owner_mii; + encoded_data.application_id = nfc_data.user_memory.application_id; + encoded_data.application_write_counter = nfc_data.user_memory.application_write_counter; + encoded_data.application_area_id = nfc_data.user_memory.application_area_id; + encoded_data.application_id_byte = nfc_data.user_memory.application_id_byte; + encoded_data.unknown = nfc_data.user_memory.unknown; + encoded_data.mii_extension = nfc_data.user_memory.mii_extension; + encoded_data.unknown2 = nfc_data.user_memory.unknown2; + encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc; + encoded_data.application_area = nfc_data.user_memory.application_area; + encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; + encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; + encoded_data.model_info = nfc_data.user_memory.model_info; + encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; + encoded_data.dynamic_lock = nfc_data.dynamic_lock; + encoded_data.CFG0 = nfc_data.CFG0; + encoded_data.CFG1 = nfc_data.CFG1; + encoded_data.password = nfc_data.password; + + return encoded_data; +} + +EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { + EncryptedNTAG215File nfc_data{}; + + nfc_data.uuid.uid = encoded_data.uid; + nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; + nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; + nfc_data.static_lock = encoded_data.static_lock; + nfc_data.compability_container = encoded_data.compability_container; + nfc_data.user_memory.hmac_data = encoded_data.hmac_data; + nfc_data.user_memory.constant_value = encoded_data.constant_value; + nfc_data.user_memory.write_counter = encoded_data.write_counter; + nfc_data.user_memory.amiibo_version = encoded_data.amiibo_version; + nfc_data.user_memory.settings = encoded_data.settings; + nfc_data.user_memory.owner_mii = encoded_data.owner_mii; + nfc_data.user_memory.application_id = encoded_data.application_id; + nfc_data.user_memory.application_write_counter = encoded_data.application_write_counter; + nfc_data.user_memory.application_area_id = encoded_data.application_area_id; + nfc_data.user_memory.application_id_byte = encoded_data.application_id_byte; + nfc_data.user_memory.unknown = encoded_data.unknown; + nfc_data.user_memory.mii_extension = encoded_data.mii_extension; + nfc_data.user_memory.unknown2 = encoded_data.unknown2; + nfc_data.user_memory.register_info_crc = encoded_data.register_info_crc; + nfc_data.user_memory.application_area = encoded_data.application_area; + nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; + nfc_data.user_memory.model_info = encoded_data.model_info; + nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt; + nfc_data.dynamic_lock = encoded_data.dynamic_lock; + nfc_data.CFG0 = encoded_data.CFG0; + nfc_data.CFG1 = encoded_data.CFG1; + nfc_data.password = encoded_data.password; + + return nfc_data; +} + +u32 GetTagPassword(const TagUuid& uuid) { + // Verify that the generated password is correct + u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); + password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; + password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; + password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; + return password; +} + +HashSeed GetSeed(const NTAG215File& data) { + HashSeed seed{ + .magic = data.write_counter, + .padding = {}, + .uid_1 = data.uid, + .nintendo_id_1 = data.nintendo_id, + .uid_2 = data.uid, + .nintendo_id_2 = data.nintendo_id, + .keygen_salt = data.keygen_salt, + }; + + return seed; +} + +std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed) { + const std::size_t seedPart1Len = sizeof(key.magic_bytes) - key.magic_length; + const std::size_t string_size = key.type_string.size(); + std::vector output(string_size + seedPart1Len); + + // Copy whole type string + memccpy(output.data(), key.type_string.data(), '\0', string_size); + + // Append (16 - magic_length) from the input seed + memcpy(output.data() + string_size, &seed, seedPart1Len); + + // Append all bytes from magicBytes + output.insert(output.end(), key.magic_bytes.begin(), + key.magic_bytes.begin() + key.magic_length); + + output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); + output.emplace_back(seed.nintendo_id_1); + output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); + output.emplace_back(seed.nintendo_id_2); + + for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { + output.emplace_back(static_cast(seed.keygen_salt[i] ^ key.xor_pad[i])); + } + + return output; +} + +void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, + const std::vector& seed) { + // Initialize context + ctx.used = false; + ctx.counter = 0; + ctx.buffer_size = sizeof(ctx.counter) + seed.size(); + memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size()); + + // Initialize HMAC context + mbedtls_md_init(&hmac_ctx); + mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); + mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size()); +} + +void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) { + // If used at least once, reinitialize the HMAC + if (ctx.used) { + mbedtls_md_hmac_reset(&hmac_ctx); + } + + ctx.used = true; + + // Store counter in big endian, and increment it + ctx.buffer[0] = static_cast(ctx.counter >> 8); + ctx.buffer[1] = static_cast(ctx.counter >> 0); + ctx.counter++; + + // Do HMAC magic + mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast(ctx.buffer.data()), + ctx.buffer_size); + mbedtls_md_hmac_finish(&hmac_ctx, output.data()); +} + +DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) { + const auto seed = GetSeed(data); + + // Generate internal seed + const std::vector internal_key = GenerateInternalKey(key, seed); + + // Initialize context + CryptoCtx ctx{}; + mbedtls_md_context_t hmac_ctx; + CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key); + + // Generate derived keys + DerivedKeys derived_keys{}; + std::array temp{}; + CryptoStep(ctx, hmac_ctx, temp[0]); + CryptoStep(ctx, hmac_ctx, temp[1]); + memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys)); + + // Cleanup context + mbedtls_md_free(&hmac_ctx); + + return derived_keys; +} + +void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) { + mbedtls_aes_context aes; + std::size_t nc_off = 0; + std::array nonce_counter{}; + std::array stream_block{}; + + const auto aes_key_size = static_cast(keys.aes_key.size() * 8); + mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), aes_key_size); + memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(keys.aes_iv)); + + constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START; + mbedtls_aes_crypt_ctr(&aes, encrypted_data_size, &nc_off, nonce_counter.data(), + stream_block.data(), + reinterpret_cast(&in_data.settings), + reinterpret_cast(&out_data.settings)); + + // Copy the rest of the data directly + out_data.uid = in_data.uid; + out_data.nintendo_id = in_data.nintendo_id; + out_data.lock_bytes = in_data.lock_bytes; + out_data.static_lock = in_data.static_lock; + out_data.compability_container = in_data.compability_container; + + out_data.constant_value = in_data.constant_value; + out_data.write_counter = in_data.write_counter; + + out_data.model_info = in_data.model_info; + out_data.keygen_salt = in_data.keygen_salt; + out_data.dynamic_lock = in_data.dynamic_lock; + out_data.CFG0 = in_data.CFG0; + out_data.CFG1 = in_data.CFG1; + out_data.password = in_data.password; +} + +bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { + const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + + const Common::FS::IOFile keys_file{yuzu_keys_dir / "key_retail.bin", + Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (!keys_file.IsOpen()) { + LOG_ERROR(Service_NFP, "Failed to open key file"); + return false; + } + + if (keys_file.Read(unfixed_info) != 1) { + LOG_ERROR(Service_NFP, "Failed to read unfixed_info"); + return false; + } + if (keys_file.Read(locked_secret) != 1) { + LOG_ERROR(Service_NFP, "Failed to read locked-secret"); + return false; + } + + return true; +} + +bool IsKeyAvailable() { + const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin"); +} + +bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { + InternalKey locked_secret{}; + InternalKey unfixed_info{}; + + if (!LoadKeys(locked_secret, unfixed_info)) { + return false; + } + + // Generate keys + NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data); + const auto data_keys = GenerateKey(unfixed_info, encoded_data); + const auto tag_keys = GenerateKey(locked_secret, encoded_data); + + // Decrypt + Cipher(data_keys, encoded_data, tag_data); + + // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! + constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), + sizeof(HmacKey), reinterpret_cast(&tag_data.uid), + input_length, reinterpret_cast(&tag_data.hmac_tag)); + + // Regenerate data HMAC + constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(), + sizeof(HmacKey), + reinterpret_cast(&tag_data.write_counter), input_length2, + reinterpret_cast(&tag_data.hmac_data)); + + if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) { + LOG_ERROR(Service_NFP, "hmac_data doesn't match"); + return false; + } + + if (tag_data.hmac_tag != encrypted_tag_data.user_memory.hmac_tag) { + LOG_ERROR(Service_NFP, "hmac_tag doesn't match"); + return false; + } + + return true; +} + +bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) { + InternalKey locked_secret{}; + InternalKey unfixed_info{}; + + if (!LoadKeys(locked_secret, unfixed_info)) { + return false; + } + + // Generate keys + const auto data_keys = GenerateKey(unfixed_info, tag_data); + const auto tag_keys = GenerateKey(locked_secret, tag_data); + + NTAG215File encoded_tag_data{}; + + // Generate tag HMAC + constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; + constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), + sizeof(HmacKey), reinterpret_cast(&tag_data.uid), + input_length, reinterpret_cast(&encoded_tag_data.hmac_tag)); + + // Init mbedtls HMAC context + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); + + // Generate data HMAC + mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey)); + mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.write_counter), + input_length2); // Data + mbedtls_md_hmac_update(&ctx, reinterpret_cast(&encoded_tag_data.hmac_tag), + sizeof(HashData)); // Tag HMAC + mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.uid), + input_length); + mbedtls_md_hmac_finish(&ctx, reinterpret_cast(&encoded_tag_data.hmac_data)); + + // HMAC cleanup + mbedtls_md_free(&ctx); + + // Encrypt + Cipher(data_keys, tag_data, encoded_tag_data); + + // Convert back to hardware + encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data); + + return true; +} + +} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h new file mode 100644 index 000000000..bf3044ed9 --- /dev/null +++ b/src/core/hle/service/nfc/common/amiibo_crypto.h @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "core/hle/service/nfp/nfp_types.h" + +struct mbedtls_md_context_t; + +namespace Service::NFP::AmiiboCrypto { +// Byte locations in Service::NFP::NTAG215File +constexpr std::size_t HMAC_DATA_START = 0x8; +constexpr std::size_t SETTINGS_START = 0x2c; +constexpr std::size_t WRITE_COUNTER_START = 0x29; +constexpr std::size_t HMAC_TAG_START = 0x1B4; +constexpr std::size_t UUID_START = 0x1D4; +constexpr std::size_t DYNAMIC_LOCK_START = 0x208; + +using HmacKey = std::array; +using DrgbOutput = std::array; + +struct HashSeed { + u16_be magic; + std::array padding; + NFC::UniqueSerialNumber uid_1; + u8 nintendo_id_1; + NFC::UniqueSerialNumber uid_2; + u8 nintendo_id_2; + std::array keygen_salt; +}; +static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); + +struct InternalKey { + HmacKey hmac_key; + std::array type_string; + u8 reserved; + u8 magic_length; + std::array magic_bytes; + std::array xor_pad; +}; +static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size"); +static_assert(std::is_trivially_copyable_v, "InternalKey must be trivially copyable."); + +struct CryptoCtx { + std::array buffer; + bool used; + std::size_t buffer_size; + s16 counter; +}; + +struct DerivedKeys { + std::array aes_key; + std::array aes_iv; + std::array hmac_key; +}; +static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); + +/// Validates that the amiibo file is not corrupted +bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file); + +/// Validates that the amiibo file is not corrupted +bool IsAmiiboValid(const NTAG215File& ntag_file); + +/// Converts from encrypted file format to encoded file format +NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data); + +/// Converts from encoded file format to encrypted file format +EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); + +/// Returns password needed to allow write access to protected memory +u32 GetTagPassword(const TagUuid& uuid); + +// Generates Seed needed for key derivation +HashSeed GetSeed(const NTAG215File& data); + +// Middle step on the generation of derived keys +std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed); + +// Initializes mbedtls context +void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, + const std::vector& seed); + +// Feeds data to mbedtls context to generate the derived key +void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output); + +// Generates the derived key from amiibo data +DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data); + +// Encodes or decodes amiibo data +void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data); + +/// Loads both amiibo keys from key_retail.bin +bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); + +/// Returns true if key_retail.bin exist +bool IsKeyAvailable(); + +/// Decodes encrypted amiibo data returns true if output is valid +bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); + +/// Encodes plain amiibo data returns true if output is valid +bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data); + +} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp new file mode 100644 index 000000000..e5de65ce0 --- /dev/null +++ b/src/core/hle/service/nfc/common/device.cpp @@ -0,0 +1,1249 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "common/input.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/tiny_mt.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/mii/types.h" +#include "core/hle/service/nfc/common/amiibo_crypto.h" +#include "core/hle/service/nfc/common/device.h" +#include "core/hle/service/nfc/mifare_result.h" +#include "core/hle/service/nfc/nfc_result.h" +#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/time/time_zone_content_manager.h" +#include "core/hle/service/time/time_zone_types.h" + +namespace Service::NFC { +NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_) + : npad_id{npad_id_}, system{system_}, service_context{service_context_}, + availability_change_event{availability_change_event_} { + activate_event = service_context.CreateEvent("NFC:ActivateEvent"); + deactivate_event = service_context.CreateEvent("NFC:DeactivateEvent"); + npad_device = system.HIDCore().GetEmulatedController(npad_id); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, + .is_npad_service = false, + }; + is_controller_set = true; + callback_key = npad_device->SetCallback(engine_callback); + + auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; + current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; +} + +NfcDevice::~NfcDevice() { + service_context.CloseEvent(activate_event); + service_context.CloseEvent(deactivate_event); + if (!is_controller_set) { + return; + } + npad_device->DeleteCallback(callback_key); + is_controller_set = false; +}; + +void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { + if (!is_initalized) { + return; + } + + if (type == Core::HID::ControllerTriggerType::Connected) { + Initialize(); + availability_change_event->Signal(); + return; + } + + if (type == Core::HID::ControllerTriggerType::Disconnected) { + device_state = DeviceState::Unavailable; + availability_change_event->Signal(); + return; + } + + if (type != Core::HID::ControllerTriggerType::Nfc) { + return; + } + + if (!npad_device->IsConnected()) { + return; + } + + const auto nfc_status = npad_device->GetNfc(); + switch (nfc_status.state) { + case Common::Input::NfcState::NewAmiibo: + LoadNfcTag(nfc_status.data); + break; + case Common::Input::NfcState::AmiiboRemoved: + if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { + break; + } + if (device_state != DeviceState::SearchingForTag) { + CloseNfcTag(); + } + break; + default: + break; + } +} + +bool NfcDevice::LoadNfcTag(std::span data) { + if (device_state != DeviceState::SearchingForTag) { + LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state); + return false; + } + + if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { + LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); + return false; + } + + mifare_data.resize(data.size()); + memcpy(mifare_data.data(), data.data(), data.size()); + + memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); + is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data); + + if (is_plain_amiibo) { + encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(tag_data); + LOG_INFO(Service_NFP, "Using plain amiibo"); + } else { + tag_data = {}; + memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); + } + + device_state = DeviceState::TagFound; + deactivate_event->GetReadableEvent().Clear(); + activate_event->Signal(); + return true; +} + +void NfcDevice::CloseNfcTag() { + LOG_INFO(Service_NFC, "Remove nfc tag"); + + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + + device_state = DeviceState::TagRemoved; + encrypted_tag_data = {}; + tag_data = {}; + mifare_data = {}; + activate_event->GetReadableEvent().Clear(); + deactivate_event->Signal(); +} + +Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const { + return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const { + return deactivate_event->GetReadableEvent(); +} + +void NfcDevice::Initialize() { + device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; + encrypted_tag_data = {}; + tag_data = {}; + mifare_data = {}; + is_initalized = true; +} + +void NfcDevice::Finalize() { + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + StopDetection(); + } + device_state = DeviceState::Unavailable; + is_initalized = false; +} + +Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) { + if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + return ResultWrongDeviceState; + } + + if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::NFC) != + Common::Input::DriverResult::Success) { + LOG_ERROR(Service_NFC, "Nfc not supported"); + return ResultNfcDisabled; + } + + device_state = DeviceState::SearchingForTag; + allowed_protocols = allowed_protocol; + return ResultSuccess; +} + +Result NfcDevice::StopDetection() { + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::Active); + + if (device_state == DeviceState::Initialized) { + return ResultSuccess; + } + + if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { + CloseNfcTag(); + } + + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + device_state = DeviceState::Initialized; + return ResultSuccess; + } + + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + return ResultWrongDeviceState; +} + +Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (is_mifare) { + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_extension = {}, + .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), + .protocol = NfcProtocol::TypeA, + .tag_type = TagType::Type4, + }; + return ResultSuccess; + } + + // Protocol and tag type may change here + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_extension = {}, + .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), + .protocol = NfcProtocol::TypeA, + .tag_type = TagType::Type2, + }; + + return ResultSuccess; +} + +Result NfcDevice::ReadMifare(std::span parameters, + std::span read_block_data) const { + Result result = ResultSuccess; + + for (std::size_t i = 0; i < parameters.size(); i++) { + result = ReadMifare(parameters[i], read_block_data[i]); + if (result.IsError()) { + break; + } + } + + return result; +} + +Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter, + MifareReadBlockData& read_block_data) const { + const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); + read_block_data.sector_number = parameter.sector_number; + + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mifare_data.size() < sector_index + sizeof(DataBlock)) { + return Mifare::ResultReadError; + } + + // TODO: Use parameter.sector_key to read encrypted data + memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock)); + + return ResultSuccess; +} + +Result NfcDevice::WriteMifare(std::span parameters) { + Result result = ResultSuccess; + + for (std::size_t i = 0; i < parameters.size(); i++) { + result = WriteMifare(parameters[i]); + if (result.IsError()) { + break; + } + } + + if (!npad_device->WriteNfc(mifare_data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return Mifare::ResultReadError; + } + + return result; +} + +Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) { + const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); + + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mifare_data.size() < sector_index + sizeof(DataBlock)) { + return Mifare::ResultReadError; + } + + // TODO: Use parameter.sector_key to encrypt the data + memcpy(mifare_data.data() + sector_index, parameter.data.data(), sizeof(DataBlock)); + + return ResultSuccess; +} + +Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, + std::span command_data, + std::span out_data) { + // Not implemented + return ResultSuccess; +} + +Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) { + if (device_state != DeviceState::TagFound) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return ResultWrongDeviceState; + } + + // The loaded amiibo is not encrypted + if (is_plain_amiibo) { + device_state = DeviceState::TagMounted; + mount_target = mount_target_; + return ResultSuccess; + } + + if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Not an amiibo"); + return ResultNotAnAmiibo; + } + + // Mark amiibos as read only when keys are missing + if (!NFP::AmiiboCrypto::IsKeyAvailable()) { + LOG_ERROR(Service_NFP, "No keys detected"); + device_state = DeviceState::TagMounted; + mount_target = NFP::MountTarget::Rom; + return ResultSuccess; + } + + if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); + return ResultCorruptedData; + } + + device_state = DeviceState::TagMounted; + mount_target = mount_target_; + return ResultSuccess; +} + +Result NfcDevice::Unmount() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + // Save data before unloading the amiibo + if (is_data_moddified) { + Flush(); + } + + device_state = DeviceState::TagFound; + mount_target = NFP::MountTarget::None; + is_app_area_open = false; + + return ResultSuccess; +} + +Result NfcDevice::Flush() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + auto& settings = tag_data.settings; + + const auto& current_date = GetAmiiboDate(current_posix_time); + if (settings.write_date.raw_date != current_date.raw_date) { + settings.write_date = current_date; + UpdateSettingsCrc(); + } + + tag_data.write_counter++; + + FlushWithBreak(NFP::BreakType::Normal); + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfcDevice::FlushDebug() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + tag_data.write_counter++; + + FlushWithBreak(NFP::BreakType::Normal); + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { + if (break_type != NFP::BreakType::Normal) { + LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type); + return ResultWrongDeviceState; + } + + std::vector data(sizeof(NFP::EncryptedNTAG215File)); + if (is_plain_amiibo) { + memcpy(data.data(), &tag_data, sizeof(tag_data)); + } else { + if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Failed to encode data"); + return ResultWriteAmiiboFailed; + } + + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + } + + if (!npad_device->WriteNfc(data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return ResultWriteAmiiboFailed; + } + + return ResultSuccess; +} + +Result NfcDevice::Restore() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + // TODO: Load amiibo from backup on system + LOG_ERROR(Service_NFP, "Not Implemented"); + return ResultSuccess; +} + +Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + const auto& settings = tag_data.settings; + + // TODO: Validate this data + common_info = { + .last_write_date = settings.write_date.GetWriteDate(), + .write_counter = tag_data.write_counter, + .version = tag_data.amiibo_version, + .application_area_size = sizeof(NFP::ApplicationArea), + }; + return ResultSuccess; +} + +Result NfcDevice::GetModelInfo(NFP::ModelInfo& model_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + const auto& model_info_data = encrypted_tag_data.user_memory.model_info; + model_info = { + .character_id = model_info_data.character_id, + .character_variant = model_info_data.character_variant, + .amiibo_type = model_info_data.amiibo_type, + .model_number = model_info_data.model_number, + .series = model_info_data.series, + }; + return ResultSuccess; +} + +Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return ResultRegistrationIsNotInitialized; + } + + Service::Mii::MiiManager manager; + const auto& settings = tag_data.settings; + + // TODO: Validate this data + register_info = { + .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), + .creation_date = settings.init_date.GetWriteDate(), + .amiibo_name = GetAmiiboName(settings), + .font_region = settings.settings.font_region, + }; + + return ResultSuccess; +} + +Result NfcDevice::GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return ResultRegistrationIsNotInitialized; + } + + Service::Mii::MiiManager manager; + const auto& settings = tag_data.settings; + + // TODO: Validate and complete this data + register_info = { + .mii_store_data = {}, + .creation_date = settings.init_date.GetWriteDate(), + .amiibo_name = GetAmiiboName(settings), + .font_region = settings.settings.font_region, + }; + + return ResultSuccess; +} + +Result NfcDevice::GetAdminInfo(NFP::AdminInfo& admin_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + u8 flags = static_cast(tag_data.settings.settings.raw >> 0x4); + if (tag_data.settings.settings.amiibo_initialized == 0) { + flags = flags & 0xfe; + } + + u64 application_id = 0; + u32 application_area_id = 0; + NFP::AppAreaVersion app_area_version = NFP::AppAreaVersion::NotSet; + if (tag_data.settings.settings.appdata_initialized != 0) { + application_id = tag_data.application_id; + app_area_version = static_cast( + application_id >> NFP::application_id_version_offset & 0xf); + + // Restore application id to original value + if (application_id >> 0x38 != 0) { + const u8 application_byte = tag_data.application_id_byte & 0xf; + application_id = + RemoveVersionByte(application_id) | + (static_cast(application_byte) << NFP::application_id_version_offset); + } + + application_area_id = tag_data.application_area_id; + } + + // TODO: Validate this data + admin_info = { + .application_id = application_id, + .application_area_id = application_area_id, + .crc_change_counter = tag_data.settings.crc_counter, + .flags = flags, + .tag_type = PackedTagType::Type2, + .app_area_version = app_area_version, + }; + + return ResultSuccess; +} + +Result NfcDevice::DeleteRegisterInfo() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return ResultRegistrationIsNotInitialized; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); + rng.GenerateRandomBytes(&tag_data.settings.amiibo_name, sizeof(tag_data.settings.amiibo_name)); + rng.GenerateRandomBytes(&tag_data.unknown, sizeof(u8)); + rng.GenerateRandomBytes(&tag_data.unknown2[0], sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.unknown2[1], sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.register_info_crc, sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.settings.init_date, sizeof(u32)); + tag_data.settings.settings.font_region.Assign(0); + tag_data.settings.settings.amiibo_initialized.Assign(0); + + return Flush(); +} + +Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + Service::Mii::MiiManager manager; + const auto mii = manager.BuildDefault(0); + auto& settings = tag_data.settings; + + if (tag_data.settings.settings.amiibo_initialized == 0) { + settings.init_date = GetAmiiboDate(current_posix_time); + settings.write_date.raw_date = 0; + } + + SetAmiiboName(settings, register_info.amiibo_name); + tag_data.owner_mii = manager.BuildFromStoreData(mii); + tag_data.mii_extension = manager.SetFromStoreData(mii); + tag_data.unknown = 0; + tag_data.unknown2 = {}; + settings.country_code_id = 0; + settings.settings.font_region.Assign(0); + settings.settings.amiibo_initialized.Assign(1); + + UpdateRegisterInfoCrc(); + + return Flush(); +} + +Result NfcDevice::RestoreAmiibo() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + // TODO: Load amiibo from backup on system + LOG_ERROR(Service_NFP, "Not Implemented"); + return ResultSuccess; +} + +Result NfcDevice::Format() { + auto result1 = DeleteApplicationArea(); + auto result2 = DeleteRegisterInfo(); + + if (result1.IsError()) { + return result1; + } + + if (result2.IsError()) { + return result2; + } + + return Flush(); +} + +Result NfcDevice::OpenApplicationArea(u32 access_id) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_WARNING(Service_NFP, "Application area is not initialized"); + return ResultApplicationAreaIsNotInitialized; + } + + if (tag_data.application_area_id != access_id) { + LOG_WARNING(Service_NFP, "Wrong application area id"); + return ResultWrongApplicationAreaId; + } + + is_app_area_open = true; + + return ResultSuccess; +} + +Result NfcDevice::GetApplicationAreaId(u32& application_area_id) const { + application_area_id = {}; + + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_WARNING(Service_NFP, "Application area is not initialized"); + return ResultApplicationAreaIsNotInitialized; + } + + application_area_id = tag_data.application_area_id; + + return ResultSuccess; +} + +Result NfcDevice::GetApplicationArea(std::span data) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ResultApplicationAreaIsNotInitialized; + } + + memcpy(data.data(), tag_data.application_area.data(), + std::min(data.size(), sizeof(NFP::ApplicationArea))); + + return ResultSuccess; +} + +Result NfcDevice::SetApplicationArea(std::span data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ResultApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(NFP::ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultUnknown; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(NFP::ApplicationArea) - data.size()); + + if (tag_data.application_write_counter != NFP::counter_limit) { + tag_data.application_write_counter++; + } + + is_data_moddified = true; + + return ResultSuccess; +} + +Result NfcDevice::CreateApplicationArea(u32 access_id, std::span data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() != 0) { + LOG_ERROR(Service_NFP, "Application area already exist"); + return ResultApplicationAreaExist; + } + + return RecreateApplicationArea(access_id, data); +} + +Result NfcDevice::RecreateApplicationArea(u32 access_id, std::span data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is open"); + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (data.size() > sizeof(NFP::ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultWrongApplicationAreaSize; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(NFP::ApplicationArea) - data.size()); + + if (tag_data.application_write_counter != NFP::counter_limit) { + tag_data.application_write_counter++; + } + + const u64 application_id = system.GetApplicationProcessProgramID(); + + tag_data.application_id_byte = + static_cast(application_id >> NFP::application_id_version_offset & 0xf); + tag_data.application_id = + RemoveVersionByte(application_id) | (static_cast(NFP::AppAreaVersion::NintendoSwitch) + << NFP::application_id_version_offset); + tag_data.settings.settings.appdata_initialized.Assign(1); + tag_data.application_area_id = access_id; + tag_data.unknown = {}; + tag_data.unknown2 = {}; + + UpdateRegisterInfoCrc(); + + return Flush(); +} + +Result NfcDevice::DeleteApplicationArea() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized == 0) { + return ResultApplicationAreaIsNotInitialized; + } + + if (tag_data.application_write_counter != NFP::counter_limit) { + tag_data.application_write_counter++; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(NFP::ApplicationArea)); + rng.GenerateRandomBytes(&tag_data.application_id, sizeof(u64)); + rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); + rng.GenerateRandomBytes(&tag_data.application_id_byte, sizeof(u8)); + tag_data.settings.settings.appdata_initialized.Assign(0); + tag_data.unknown = {}; + tag_data.unknown2 = {}; + is_app_area_open = false; + + UpdateRegisterInfoCrc(); + + return Flush(); +} + +Result NfcDevice::ExistsApplicationArea(bool& has_application_area) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0; + + return ResultSuccess; +} + +Result NfcDevice::GetAll(NFP::NfpData& data) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + NFP::CommonInfo common_info{}; + Service::Mii::MiiManager manager; + const u64 application_id = tag_data.application_id; + + GetCommonInfo(common_info); + + data = { + .magic = tag_data.constant_value, + .write_counter = tag_data.write_counter, + .settings_crc = tag_data.settings.crc, + .common_info = common_info, + .mii_char_info = tag_data.owner_mii, + .mii_store_data_extension = tag_data.mii_extension, + .creation_date = tag_data.settings.init_date.GetWriteDate(), + .amiibo_name = tag_data.settings.amiibo_name, + .amiibo_name_null_terminated = 0, + .settings = tag_data.settings.settings, + .unknown1 = tag_data.unknown, + .register_info_crc = tag_data.register_info_crc, + .unknown2 = tag_data.unknown2, + .application_id = application_id, + .access_id = tag_data.application_area_id, + .settings_crc_counter = tag_data.settings.crc_counter, + .font_region = tag_data.settings.settings.font_region, + .tag_type = PackedTagType::Type2, + .console_type = static_cast( + application_id >> NFP::application_id_version_offset & 0xf), + .application_id_byte = tag_data.application_id_byte, + .application_area = tag_data.application_area, + }; + + return ResultSuccess; +} + +Result NfcDevice::SetAll(const NFP::NfpData& data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + tag_data.constant_value = data.magic; + tag_data.write_counter = data.write_counter; + tag_data.settings.crc = data.settings_crc; + tag_data.settings.write_date.SetWriteDate(data.common_info.last_write_date); + tag_data.write_counter = data.common_info.write_counter; + tag_data.amiibo_version = data.common_info.version; + tag_data.owner_mii = data.mii_char_info; + tag_data.mii_extension = data.mii_store_data_extension; + tag_data.settings.init_date.SetWriteDate(data.creation_date); + tag_data.settings.amiibo_name = data.amiibo_name; + tag_data.settings.settings = data.settings; + tag_data.unknown = data.unknown1; + tag_data.register_info_crc = data.register_info_crc; + tag_data.unknown2 = data.unknown2; + tag_data.application_id = data.application_id; + tag_data.application_area_id = data.access_id; + tag_data.settings.crc_counter = data.settings_crc_counter; + tag_data.settings.settings.font_region.Assign(data.font_region); + tag_data.application_id_byte = data.application_id_byte; + tag_data.application_area = data.application_area; + + return ResultSuccess; +} + +Result NfcDevice::BreakTag(NFP::BreakType break_type) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + // TODO: Complete this implementation + + return FlushWithBreak(break_type); +} + +Result NfcDevice::ReadBackupData(std::span data) const { + // Not implemented + return ResultSuccess; +} + +Result NfcDevice::WriteBackupData(std::span data) { + // Not implemented + return ResultSuccess; +} + +Result NfcDevice::WriteNtf(std::span data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return ResultTagRemoved; + } + return ResultWrongDeviceState; + } + + if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return ResultWrongDeviceState; + } + + // Not implemented + + return ResultSuccess; +} + +NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) const { + std::array settings_amiibo_name{}; + NFP::AmiiboName amiibo_name{}; + + // Convert from big endian to little endian + for (std::size_t i = 0; i < NFP::amiibo_name_length; i++) { + settings_amiibo_name[i] = static_cast(settings.amiibo_name[i]); + } + + // Convert from utf16 to utf8 + const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); + memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); + + return amiibo_name; +} + +void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) { + std::array settings_amiibo_name{}; + + // Convert from utf8 to utf16 + const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); + memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), + amiibo_name_utf16.size() * sizeof(char16_t)); + + // Convert from little endian to big endian + for (std::size_t i = 0; i < NFP::amiibo_name_length; i++) { + settings.amiibo_name[i] = static_cast(settings_amiibo_name[i]); + } +} + +NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const { + const auto& time_zone_manager = + system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + Time::TimeZone::CalendarInfo calendar_info{}; + NFP::AmiiboDate amiibo_date{}; + + amiibo_date.SetYear(2000); + amiibo_date.SetMonth(1); + amiibo_date.SetDay(1); + + if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { + amiibo_date.SetYear(calendar_info.time.year); + amiibo_date.SetMonth(calendar_info.time.month); + amiibo_date.SetDay(calendar_info.time.day); + } + + return amiibo_date; +} + +u64 NfcDevice::RemoveVersionByte(u64 application_id) const { + return application_id & ~(0xfULL << NFP::application_id_version_offset); +} + +void NfcDevice::UpdateSettingsCrc() { + auto& settings = tag_data.settings; + + if (settings.crc_counter != NFP::counter_limit) { + settings.crc_counter++; + } + + // TODO: this reads data from a global, find what it is + std::array unknown_input{}; + boost::crc_32_type crc; + crc.process_bytes(&unknown_input, sizeof(unknown_input)); + settings.crc = crc.checksum(); +} + +void NfcDevice::UpdateRegisterInfoCrc() { +#pragma pack(push, 1) + struct CrcData { + Mii::Ver3StoreData mii; + u8 application_id_byte; + u8 unknown; + Mii::NfpStoreDataExtension mii_extension; + std::array unknown2; + }; + static_assert(sizeof(CrcData) == 0x7e, "CrcData is an invalid size"); +#pragma pack(pop) + + const CrcData crc_data{ + .mii = tag_data.owner_mii, + .application_id_byte = tag_data.application_id_byte, + .unknown = tag_data.unknown, + .mii_extension = tag_data.mii_extension, + .unknown2 = tag_data.unknown2, + }; + + boost::crc_32_type crc; + crc.process_bytes(&crc_data, sizeof(CrcData)); + tag_data.register_info_crc = crc.checksum(); +} + +u64 NfcDevice::GetHandle() const { + // Generate a handle based of the npad id + return static_cast(npad_id); +} + +DeviceState NfcDevice::GetCurrentState() const { + return device_state; +} + +Result NfcDevice::GetNpadId(Core::HID::NpadIdType& out_npad_id) const { + out_npad_id = npad_id; + return ResultSuccess; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h new file mode 100644 index 000000000..654eda98e --- /dev/null +++ b/src/core/hle/service/nfc/common/device.h @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfc/mifare_types.h" +#include "core/hle/service/nfc/nfc_types.h" +#include "core/hle/service/nfp/nfp_types.h" +#include "core/hle/service/service.h" +#include "core/hle/service/time/clock_types.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Core::HID { +class EmulatedController; +enum class ControllerTriggerType; +enum class NpadIdType : u32; +} // namespace Core::HID + +namespace Service::NFC { +class NfcDevice { +public: + NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_); + ~NfcDevice(); + + void Initialize(); + void Finalize(); + + Result StartDetection(NfcProtocol allowed_protocol); + Result StopDetection(); + + Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const; + + Result ReadMifare(std::span parameters, + std::span read_block_data) const; + Result ReadMifare(const MifareReadBlockParameter& parameter, + MifareReadBlockData& read_block_data) const; + + Result WriteMifare(std::span parameters); + Result WriteMifare(const MifareWriteBlockParameter& parameter); + + Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, + std::span command_data, std::span out_data); + + Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target); + Result Unmount(); + + Result Flush(); + Result FlushDebug(); + Result FlushWithBreak(NFP::BreakType break_type); + Result Restore(); + + Result GetCommonInfo(NFP::CommonInfo& common_info) const; + Result GetModelInfo(NFP::ModelInfo& model_info) const; + Result GetRegisterInfo(NFP::RegisterInfo& register_info) const; + Result GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const; + Result GetAdminInfo(NFP::AdminInfo& admin_info) const; + + Result DeleteRegisterInfo(); + Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info); + Result RestoreAmiibo(); + Result Format(); + + Result OpenApplicationArea(u32 access_id); + Result GetApplicationAreaId(u32& application_area_id) const; + Result GetApplicationArea(std::span data) const; + Result SetApplicationArea(std::span data); + Result CreateApplicationArea(u32 access_id, std::span data); + Result RecreateApplicationArea(u32 access_id, std::span data); + Result DeleteApplicationArea(); + Result ExistsApplicationArea(bool& has_application_area) const; + + Result GetAll(NFP::NfpData& data) const; + Result SetAll(const NFP::NfpData& data); + Result BreakTag(NFP::BreakType break_type); + Result ReadBackupData(std::span data) const; + Result WriteBackupData(std::span data); + Result WriteNtf(std::span data); + + u64 GetHandle() const; + DeviceState GetCurrentState() const; + Result GetNpadId(Core::HID::NpadIdType& out_npad_id) const; + + Kernel::KReadableEvent& GetActivateEvent() const; + Kernel::KReadableEvent& GetDeactivateEvent() const; + +private: + void NpadUpdate(Core::HID::ControllerTriggerType type); + bool LoadNfcTag(std::span data); + void CloseNfcTag(); + + NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; + void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name); + NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; + u64 RemoveVersionByte(u64 application_id) const; + void UpdateSettingsCrc(); + void UpdateRegisterInfoCrc(); + + bool is_controller_set{}; + int callback_key; + const Core::HID::NpadIdType npad_id; + Core::System& system; + Core::HID::EmulatedController* npad_device = nullptr; + KernelHelpers::ServiceContext& service_context; + Kernel::KEvent* activate_event = nullptr; + Kernel::KEvent* deactivate_event = nullptr; + Kernel::KEvent* availability_change_event = nullptr; + + bool is_initalized{}; + NfcProtocol allowed_protocols{}; + DeviceState device_state{DeviceState::Unavailable}; + + // NFP data + bool is_data_moddified{}; + bool is_app_area_open{}; + bool is_plain_amiibo{}; + s64 current_posix_time{}; + NFP::MountTarget mount_target{NFP::MountTarget::None}; + + NFP::NTAG215File tag_data{}; + std::vector mifare_data{}; + NFP::EncryptedNTAG215File encrypted_tag_data{}; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp new file mode 100644 index 000000000..d5deaaf27 --- /dev/null +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -0,0 +1,695 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/nfc/common/device.h" +#include "core/hle/service/nfc/common/device_manager.h" +#include "core/hle/service/nfc/nfc_result.h" +#include "core/hle/service/time/clock_types.h" + +namespace Service::NFC { + +DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_) + : system{system_}, service_context{service_context_} { + + availability_change_event = + service_context.CreateEvent("Nfc:DeviceManager:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < devices.size(); device_index++) { + devices[device_index] = + std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } + + is_initialized = false; +} + +DeviceManager ::~DeviceManager() { + service_context.CloseEvent(availability_change_event); +} + +Result DeviceManager::Initialize() { + for (auto& device : devices) { + device->Initialize(); + } + is_initialized = true; + return ResultSuccess; +} + +Result DeviceManager::Finalize() { + for (auto& device : devices) { + device->Finalize(); + } + is_initialized = false; + return ResultSuccess; +} + +Result DeviceManager::ListDevices(std::vector& nfp_devices, + std::size_t max_allowed_devices) const { + for (auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.empty()) { + return ResultDeviceNotFound; + } + + return ResultSuccess; +} + +DeviceState DeviceManager::GetDeviceState(u64 device_handle) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + const auto result = GetDeviceFromHandle(device_handle, device, false); + + if (result.IsSuccess()) { + return device->GetCurrentState(); + } + + return DeviceState::Unavailable; +} + +Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetNpadId(npad_id); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Kernel::KReadableEvent& DeviceManager::AttachAvailabilityChangeEvent() const { + return availability_change_event->GetReadableEvent(); +} + +Result DeviceManager::StartDetection(u64 device_handle, NfcProtocol tag_protocol) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->StartDetection(tag_protocol); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::StopDetection(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->StopDetection(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetTagInfo(tag_info, is_mifare); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + GetDeviceFromHandle(device_handle, device, false); + + // TODO: Return proper error code on failure + return device->GetActivateEvent(); +} + +Kernel::KReadableEvent& DeviceManager::AttachDeactivateEvent(u64 device_handle) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + GetDeviceFromHandle(device_handle, device, false); + + // TODO: Return proper error code on failure + return device->GetDeactivateEvent(); +} + +Result DeviceManager::ReadMifare(u64 device_handle, + std::span read_parameters, + std::span read_data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->ReadMifare(read_parameters, read_data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::WriteMifare(u64 device_handle, + std::span write_parameters) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->WriteMifare(write_parameters); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::SendCommandByPassThrough(u64 device_handle, + const Time::Clock::TimeSpanType& timeout, + std::span command_data, + std::span out_data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->SendCommandByPassThrough(timeout, command_data, out_data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::Mount(u64 device_handle, NFP::ModelType model_type, + NFP::MountTarget mount_target) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->Mount(model_type, mount_target); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::Unmount(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->Unmount(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::OpenApplicationArea(u64 device_handle, u32 access_id) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->OpenApplicationArea(access_id); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetApplicationArea(u64 device_handle, std::span data) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetApplicationArea(data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::SetApplicationArea(u64 device_handle, std::span data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->SetApplicationArea(data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::Flush(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->Flush(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::Restore(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->Restore(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::CreateApplicationArea(u64 device_handle, u32 access_id, + std::span data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->CreateApplicationArea(access_id, data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetRegisterInfo(register_info); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetCommonInfo(common_info); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetModelInfo(model_info); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +u32 DeviceManager::GetApplicationAreaSize() const { + return sizeof(NFP::ApplicationArea); +} + +Result DeviceManager::RecreateApplicationArea(u64 device_handle, u32 access_id, + std::span data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->RecreateApplicationArea(access_id, data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::Format(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->Format(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetAdminInfo(admin_info); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle, + NFP::RegisterInfoPrivate& register_info) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetRegisterInfoPrivate(register_info); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::SetRegisterInfoPrivate(u64 device_handle, + const NFP::RegisterInfoPrivate& register_info) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->SetRegisterInfoPrivate(register_info); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::DeleteRegisterInfo(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->DeleteRegisterInfo(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::DeleteApplicationArea(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->DeleteApplicationArea(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->ExistsApplicationArea(has_application_area); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->GetAll(nfp_data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::SetAll(u64 device_handle, const NFP::NfpData& nfp_data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->SetAll(nfp_data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::FlushDebug(u64 device_handle) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->FlushDebug(); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::BreakTag(u64 device_handle, NFP::BreakType break_type) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->BreakTag(break_type); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::ReadBackupData(u64 device_handle, std::span data) const { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->ReadBackupData(data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::WriteBackupData(u64 device_handle, std::span data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->WriteBackupData(data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::WriteNtf(u64 device_handle, NFP::WriteType, std::span data) { + std::scoped_lock lock{mutex}; + + std::shared_ptr device = nullptr; + auto result = GetDeviceHandle(device_handle, device); + + if (result.IsSuccess()) { + result = device->WriteNtf(data); + result = VerifyDeviceResult(device, result); + } + + return result; +} + +Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr& nfc_device, + bool check_state) const { + if (check_state) { + const Result is_parameter_set = IsNfcParameterSet(); + if (is_parameter_set.IsError()) { + return is_parameter_set; + } + const Result is_enabled = IsNfcEnabled(); + if (is_enabled.IsError()) { + return is_enabled; + } + const Result is_nfc_initialized = IsNfcInitialized(); + if (is_nfc_initialized.IsError()) { + return is_nfc_initialized; + } + } + + for (auto& device : devices) { + if (device->GetHandle() == handle) { + nfc_device = device; + return ResultSuccess; + } + } + + return ResultDeviceNotFound; +} + +std::optional> DeviceManager::GetNfcDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} + +const std::optional> DeviceManager::GetNfcDevice(u64 handle) const { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} + +Result DeviceManager::GetDeviceHandle(u64 handle, std::shared_ptr& device) const { + const auto result = GetDeviceFromHandle(handle, device, true); + if (result.IsError()) { + return result; + } + return CheckDeviceState(device); +} + +Result DeviceManager::VerifyDeviceResult(std::shared_ptr device, + Result operation_result) const { + if (operation_result.IsSuccess()) { + return operation_result; + } + + const Result is_parameter_set = IsNfcParameterSet(); + if (is_parameter_set.IsError()) { + return is_parameter_set; + } + const Result is_enabled = IsNfcEnabled(); + if (is_enabled.IsError()) { + return is_enabled; + } + const Result is_nfc_initialized = IsNfcInitialized(); + if (is_nfc_initialized.IsError()) { + return is_nfc_initialized; + } + const Result device_state = CheckDeviceState(device); + if (device_state.IsError()) { + return device_state; + } + + return operation_result; +} + +Result DeviceManager::CheckDeviceState(std::shared_ptr device) const { + if (device == nullptr) { + return ResultInvalidArgument; + } + + return ResultSuccess; +} + +Result DeviceManager::IsNfcEnabled() const { + // TODO: This calls nn::settings::detail::GetNfcEnableFlag + const bool is_enabled = true; + if (!is_enabled) { + return ResultNfcDisabled; + } + return ResultSuccess; +} + +Result DeviceManager::IsNfcParameterSet() const { + // TODO: This calls checks against a bool on offset 0x450 + const bool is_set = true; + if (!is_set) { + return ResultUnknown76; + } + return ResultSuccess; +} + +Result DeviceManager::IsNfcInitialized() const { + if (!is_initialized) { + return ResultNfcNotInitialized; + } + return ResultSuccess; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h new file mode 100644 index 000000000..2971e280f --- /dev/null +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "core/hid/hid_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfc/mifare_types.h" +#include "core/hle/service/nfc/nfc_types.h" +#include "core/hle/service/nfp/nfp_types.h" +#include "core/hle/service/service.h" +#include "core/hle/service/time/clock_types.h" + +namespace Service::NFC { +class NfcDevice; + +class DeviceManager { +public: + explicit DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_); + ~DeviceManager(); + + // Nfc device manager + Result Initialize(); + Result Finalize(); + Result ListDevices(std::vector& nfp_devices, std::size_t max_allowed_devices) const; + DeviceState GetDeviceState(u64 device_handle) const; + Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const; + Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const; + Result StartDetection(u64 device_handle, NfcProtocol tag_protocol); + Result StopDetection(u64 device_handle); + Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const; + Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const; + Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const; + Result ReadMifare(u64 device_handle, + const std::span read_parameters, + std::span read_data); + Result WriteMifare(u64 device_handle, + std::span write_parameters); + Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout, + std::span command_data, std::span out_data); + + // Nfp device manager + Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target); + Result Unmount(u64 device_handle); + Result OpenApplicationArea(u64 device_handle, u32 access_id); + Result GetApplicationArea(u64 device_handle, std::span data) const; + Result SetApplicationArea(u64 device_handle, std::span data); + Result Flush(u64 device_handle); + Result Restore(u64 device_handle); + Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span data); + Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const; + Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const; + Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const; + u32 GetApplicationAreaSize() const; + Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span data); + Result Format(u64 device_handle); + Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const; + Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info) const; + Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info); + Result DeleteRegisterInfo(u64 device_handle); + Result DeleteApplicationArea(u64 device_handle); + Result ExistsApplicationArea(u64 device_handle, bool& has_application_area) const; + Result GetAll(u64 device_handle, NFP::NfpData& nfp_data) const; + Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data); + Result FlushDebug(u64 device_handle); + Result BreakTag(u64 device_handle, NFP::BreakType break_type); + Result ReadBackupData(u64 device_handle, std::span data) const; + Result WriteBackupData(u64 device_handle, std::span data); + Result WriteNtf(u64 device_handle, NFP::WriteType, std::span data); + +private: + Result IsNfcEnabled() const; + Result IsNfcParameterSet() const; + Result IsNfcInitialized() const; + + Result GetDeviceFromHandle(u64 handle, std::shared_ptr& device, + bool check_state) const; + + Result GetDeviceHandle(u64 handle, std::shared_ptr& device) const; + Result VerifyDeviceResult(std::shared_ptr device, Result operation_result) const; + Result CheckDeviceState(std::shared_ptr device) const; + + std::optional> GetNfcDevice(u64 handle); + const std::optional> GetNfcDevice(u64 handle) const; + + bool is_initialized = false; + mutable std::mutex mutex; + std::array, 10> devices{}; + + Core::System& system; + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* availability_change_event; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_interface.cpp b/src/core/hle/service/nfc/mifare_interface.cpp deleted file mode 100644 index 7e6635ba2..000000000 --- a/src/core/hle/service/nfc/mifare_interface.cpp +++ /dev/null @@ -1,382 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hid/hid_types.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfc/mifare_interface.h" -#include "core/hle/service/nfc/nfc_device.h" -#include "core/hle/service/nfc/nfc_result.h" - -namespace Service::NFC { - -MFInterface::MFInterface(Core::System& system_, const char* name) - : ServiceFramework{system_, name}, service_context{system_, service_name} { - availability_change_event = service_context.CreateEvent("MFInterface:AvailabilityChangeEvent"); - - for (u32 device_index = 0; device_index < 10; device_index++) { - devices[device_index] = - std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, - service_context, availability_change_event); - } -} - -MFInterface ::~MFInterface() { - availability_change_event->Close(); -} - -void MFInterface::Initialize(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::Initialized; - - for (auto& device : devices) { - device->Initialize(); - } - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); -} - -void MFInterface::Finalize(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - state = State::NonInitialized; - - for (auto& device : devices) { - device->Finalize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void MFInterface::ListDevices(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - if (!ctx.CanWriteBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareInvalidArgument); - return; - } - - if (ctx.GetWriteBufferSize() == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareInvalidArgument); - return; - } - - std::vector nfp_devices; - const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); - - for (const auto& device : devices) { - if (nfp_devices.size() >= max_allowed_devices) { - continue; - } - if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { - nfp_devices.push_back(device->GetHandle()); - } - } - - if (nfp_devices.empty()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - ctx.WriteBuffer(nfp_devices); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(nfp_devices.size())); -} - -void MFInterface::StartDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - const auto result = device.value()->StartDetection(NFP::TagProtocol::All); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFInterface::StopDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - const auto result = device.value()->StopDetection(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFInterface::Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto buffer{ctx.ReadBuffer()}; - const auto number_of_commands{ctx.GetReadBufferNumElements()}; - std::vector read_commands(number_of_commands); - - memcpy(read_commands.data(), buffer.data(), - number_of_commands * sizeof(NFP::MifareReadBlockParameter)); - - LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", - device_handle, number_of_commands); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - Result result = ResultSuccess; - std::vector out_data(number_of_commands); - for (std::size_t i = 0; i < number_of_commands; i++) { - result = device.value()->MifareRead(read_commands[i], out_data[i]); - if (result.IsError()) { - break; - } - } - - ctx.WriteBuffer(out_data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFInterface::Write(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto buffer{ctx.ReadBuffer()}; - const auto number_of_commands{ctx.GetReadBufferNumElements()}; - std::vector write_commands(number_of_commands); - - memcpy(write_commands.data(), buffer.data(), - number_of_commands * sizeof(NFP::MifareWriteBlockParameter)); - - LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}", - device_handle, number_of_commands); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - Result result = ResultSuccess; - std::vector out_data(number_of_commands); - for (std::size_t i = 0; i < number_of_commands; i++) { - result = device.value()->MifareWrite(write_commands[i]); - if (result.IsError()) { - break; - } - } - - if (result.IsSuccess()) { - result = device.value()->Flush(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFInterface::GetTagInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - NFP::TagInfo tag_info{}; - const auto result = device.value()->GetTagInfo(tag_info, true); - ctx.WriteBuffer(tag_info); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void MFInterface::GetActivateEventHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetActivateEvent()); -} - -void MFInterface::GetDeactivateEventHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetDeactivateEvent()); -} - -void MFInterface::GetState(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(state); -} - -void MFInterface::GetDeviceState(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetCurrentState()); -} - -void MFInterface::GetNpadId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareDeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetNpadId()); -} - -void MFInterface::GetAvailabilityChangeEventHandle(HLERequestContext& ctx) { - LOG_INFO(Service_NFC, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(MifareNfcDisabled); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); -} - -std::optional> MFInterface::GetNfcDevice(u64 handle) { - for (auto& device : devices) { - if (device->GetHandle() == handle) { - return device; - } - } - return std::nullopt; -} - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_interface.h b/src/core/hle/service/nfc/mifare_interface.h deleted file mode 100644 index 698c8a6b6..000000000 --- a/src/core/hle/service/nfc/mifare_interface.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Service::NFC { -class NfcDevice; - -class MFInterface : public ServiceFramework { -public: - explicit MFInterface(Core::System& system_, const char* name); - ~MFInterface(); - - void Initialize(HLERequestContext& ctx); - void Finalize(HLERequestContext& ctx); - void ListDevices(HLERequestContext& ctx); - void StartDetection(HLERequestContext& ctx); - void StopDetection(HLERequestContext& ctx); - void Read(HLERequestContext& ctx); - void Write(HLERequestContext& ctx); - void GetTagInfo(HLERequestContext& ctx); - void GetActivateEventHandle(HLERequestContext& ctx); - void GetDeactivateEventHandle(HLERequestContext& ctx); - void GetState(HLERequestContext& ctx); - void GetDeviceState(HLERequestContext& ctx); - void GetNpadId(HLERequestContext& ctx); - void GetAvailabilityChangeEventHandle(HLERequestContext& ctx); - -private: - enum class State : u32 { - NonInitialized, - Initialized, - }; - - std::optional> GetNfcDevice(u64 handle); - - KernelHelpers::ServiceContext service_context; - - std::array, 10> devices{}; - - State state{State::NonInitialized}; - Kernel::KEvent* availability_change_event; -}; - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h new file mode 100644 index 000000000..4b60048a5 --- /dev/null +++ b/src/core/hle/service/nfc/mifare_result.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NFC::Mifare { + +constexpr Result ResultDeviceNotFound(ErrorModule::NFCMifare, 64); +constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65); +constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73); +constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80); +constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97); +constexpr Result ResultReadError(ErrorModule::NFCMifare, 288); + +} // namespace Service::NFC::Mifare diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h new file mode 100644 index 000000000..75b59f021 --- /dev/null +++ b/src/core/hle/service/nfc/mifare_types.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::NFC { + +enum class MifareCmd : u8 { + AuthA = 0x60, + AuthB = 0x61, + Read = 0x30, + Write = 0xA0, + Transfer = 0xB0, + Decrement = 0xC0, + Increment = 0xC1, + Store = 0xC2 +}; + +using DataBlock = std::array; +using KeyData = std::array; + +struct SectorKey { + MifareCmd command; + u8 unknown; // Usually 1 + INSERT_PADDING_BYTES(0x6); + KeyData sector_key; + INSERT_PADDING_BYTES(0x2); +}; +static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size"); + +// This is nn::nfc::MifareReadBlockParameter +struct MifareReadBlockParameter { + u8 sector_number; + INSERT_PADDING_BYTES(0x7); + SectorKey sector_key; +}; +static_assert(sizeof(MifareReadBlockParameter) == 0x18, + "MifareReadBlockParameter is an invalid size"); + +// This is nn::nfc::MifareReadBlockData +struct MifareReadBlockData { + DataBlock data; + u8 sector_number; + INSERT_PADDING_BYTES(0x7); +}; +static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); + +// This is nn::nfc::MifareWriteBlockParameter +struct MifareWriteBlockParameter { + DataBlock data; + u8 sector_number; + INSERT_PADDING_BYTES(0x7); + SectorKey sector_key; +}; +static_assert(sizeof(MifareWriteBlockParameter) == 0x28, + "MifareWriteBlockParameter is an invalid size"); + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 444d65f07..30ae989b9 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -6,7 +6,6 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfc/mifare_interface.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc_interface.h" #include "core/hle/service/server_manager.h" @@ -14,31 +13,31 @@ namespace Service::NFC { -class IUser final : public Interface { +class IUser final : public NfcInterface { public: - explicit IUser(Core::System& system_) : Interface(system_, "NFC::IUser") { + explicit IUser(Core::System& system_) : NfcInterface(system_, "NFC::IUser", BackendType::Nfc) { // clang-format off static const FunctionInfo functions[] = { - {0, &Interface::Initialize, "InitializeOld"}, - {1, &Interface::Finalize, "FinalizeOld"}, - {2, &Interface::GetState, "GetStateOld"}, - {3, &Interface::IsNfcEnabled, "IsNfcEnabledOld"}, - {400, &Interface::Initialize, "Initialize"}, - {401, &Interface::Finalize, "Finalize"}, - {402, &Interface::GetState, "GetState"}, - {403, &Interface::IsNfcEnabled, "IsNfcEnabled"}, - {404, &Interface::ListDevices, "ListDevices"}, - {405, &Interface::GetDeviceState, "GetDeviceState"}, - {406, &Interface::GetNpadId, "GetNpadId"}, - {407, &Interface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, - {408, &Interface::StartDetection, "StartDetection"}, - {409, &Interface::StopDetection, "StopDetection"}, - {410, &Interface::GetTagInfo, "GetTagInfo"}, - {411, &Interface::AttachActivateEvent, "AttachActivateEvent"}, - {412, &Interface::AttachDeactivateEvent, "AttachDeactivateEvent"}, - {1000, nullptr, "ReadMifare"}, - {1001, nullptr, "WriteMifare"}, - {1300, &Interface::SendCommandByPassThrough, "SendCommandByPassThrough"}, + {0, &NfcInterface::Initialize, "InitializeOld"}, + {1, &NfcInterface::Finalize, "FinalizeOld"}, + {2, &NfcInterface::GetState, "GetStateOld"}, + {3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"}, + {400, &NfcInterface::Initialize, "Initialize"}, + {401, &NfcInterface::Finalize, "Finalize"}, + {402, &NfcInterface::GetState, "GetState"}, + {403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"}, + {404, &NfcInterface::ListDevices, "ListDevices"}, + {405, &NfcInterface::GetDeviceState, "GetDeviceState"}, + {406, &NfcInterface::GetNpadId, "GetNpadId"}, + {407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {408, &NfcInterface::StartDetection, "StartDetection"}, + {409, &NfcInterface::StopDetection, "StopDetection"}, + {410, &NfcInterface::GetTagInfo, "GetTagInfo"}, + {411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"}, + {412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {1000, &NfcInterface::ReadMifare, "ReadMifare"}, + {1001, &NfcInterface::WriteMifare ,"WriteMifare"}, + {1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"}, {1301, nullptr, "KeepPassThroughSession"}, {1302, nullptr, "ReleasePassThroughSession"}, }; @@ -48,34 +47,35 @@ public: } }; -class ISystem final : public Interface { +class ISystem final : public NfcInterface { public: - explicit ISystem(Core::System& system_) : Interface{system_, "NFC::ISystem"} { + explicit ISystem(Core::System& system_) + : NfcInterface{system_, "NFC::ISystem", BackendType::Nfc} { // clang-format off static const FunctionInfo functions[] = { - {0, &Interface::Initialize, "InitializeOld"}, - {1, &Interface::Finalize, "FinalizeOld"}, - {2, &Interface::GetState, "GetStateOld"}, - {3, &Interface::IsNfcEnabled, "IsNfcEnabledOld"}, + {0, &NfcInterface::Initialize, "InitializeOld"}, + {1, &NfcInterface::Finalize, "FinalizeOld"}, + {2, &NfcInterface::GetState, "GetStateOld"}, + {3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"}, {100, nullptr, "SetNfcEnabledOld"}, - {400, &Interface::Initialize, "Initialize"}, - {401, &Interface::Finalize, "Finalize"}, - {402, &Interface::GetState, "GetState"}, - {403, &Interface::IsNfcEnabled, "IsNfcEnabled"}, - {404, &Interface::ListDevices, "ListDevices"}, - {405, &Interface::GetDeviceState, "GetDeviceState"}, - {406, &Interface::GetNpadId, "GetNpadId"}, - {407, &Interface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, - {408, &Interface::StartDetection, "StartDetection"}, - {409, &Interface::StopDetection, "StopDetection"}, - {410, &Interface::GetTagInfo, "GetTagInfo"}, - {411, &Interface::AttachActivateEvent, "AttachActivateEvent"}, - {412, &Interface::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {400, &NfcInterface::Initialize, "Initialize"}, + {401, &NfcInterface::Finalize, "Finalize"}, + {402, &NfcInterface::GetState, "GetState"}, + {403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"}, + {404, &NfcInterface::ListDevices, "ListDevices"}, + {405, &NfcInterface::GetDeviceState, "GetDeviceState"}, + {406, &NfcInterface::GetNpadId, "GetNpadId"}, + {407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {408, &NfcInterface::StartDetection, "StartDetection"}, + {409, &NfcInterface::StopDetection, "StopDetection"}, + {410, &NfcInterface::GetTagInfo, "GetTagInfo"}, + {411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"}, + {412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"}, {500, nullptr, "SetNfcEnabled"}, {510, nullptr, "OutputTestWave"}, - {1000, nullptr, "ReadMifare"}, - {1001, nullptr, "WriteMifare"}, - {1300, &Interface::SendCommandByPassThrough, "SendCommandByPassThrough"}, + {1000, &NfcInterface::ReadMifare, "ReadMifare"}, + {1001, &NfcInterface::WriteMifare, "WriteMifare"}, + {1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"}, {1301, nullptr, "KeepPassThroughSession"}, {1302, nullptr, "ReleasePassThroughSession"}, }; @@ -85,25 +85,29 @@ public: } }; +// MFInterface has an unique interface but it's identical to NfcInterface so we can keep the code +// simpler +using MFInterface = NfcInterface; class MFIUser final : public MFInterface { public: - explicit MFIUser(Core::System& system_) : MFInterface{system_, "NFC::MFInterface"} { + explicit MFIUser(Core::System& system_) + : MFInterface{system_, "NFC::MFInterface", BackendType::Mifare} { // clang-format off - static const FunctionInfo functions[] = { + static const FunctionInfoTyped functions[] = { {0, &MFIUser::Initialize, "Initialize"}, {1, &MFIUser::Finalize, "Finalize"}, {2, &MFIUser::ListDevices, "ListDevices"}, {3, &MFIUser::StartDetection, "StartDetection"}, {4, &MFIUser::StopDetection, "StopDetection"}, - {5, &MFIUser::Read, "Read"}, - {6, &MFIUser::Write, "Write"}, + {5, &MFIUser::ReadMifare, "Read"}, + {6, &MFIUser::WriteMifare, "Write"}, {7, &MFIUser::GetTagInfo, "GetTagInfo"}, - {8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"}, - {9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"}, + {8, &MFIUser::AttachActivateEvent, "GetActivateEventHandle"}, + {9, &MFIUser::AttachDeactivateEvent, "GetDeactivateEventHandle"}, {10, &MFIUser::GetState, "GetState"}, {11, &MFIUser::GetDeviceState, "GetDeviceState"}, {12, &MFIUser::GetNpadId, "GetNpadId"}, - {13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"}, + {13, &MFIUser::AttachAvailabilityChangeEvent, "GetAvailabilityChangeEventHandle"}, }; // clang-format on @@ -131,7 +135,7 @@ public: explicit NFC_AM(Core::System& system_) : ServiceFramework{system_, "nfc:am"} { // clang-format off static const FunctionInfo functions[] = { - {0, &NFC_AM::CreateAmInterface, "CreateAmInterface"}, + {0, &NFC_AM::CreateAmNfcInterface, "CreateAmNfcInterface"}, }; // clang-format on @@ -139,7 +143,7 @@ public: } private: - void CreateAmInterface(HLERequestContext& ctx) { + void CreateAmNfcInterface(HLERequestContext& ctx) { LOG_DEBUG(Service_NFC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -153,7 +157,7 @@ public: explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"}, + {0, &NFC_MF_U::CreateUserNfcInterface, "CreateUserNfcInterface"}, }; // clang-format on @@ -161,7 +165,7 @@ public: } private: - void CreateUserInterface(HLERequestContext& ctx) { + void CreateUserNfcInterface(HLERequestContext& ctx) { LOG_DEBUG(Service_NFC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -175,7 +179,7 @@ public: explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} { // clang-format off static const FunctionInfo functions[] = { - {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, + {0, &NFC_U::CreateUserNfcInterface, "CreateUserNfcInterface"}, }; // clang-format on @@ -183,7 +187,7 @@ public: } private: - void CreateUserInterface(HLERequestContext& ctx) { + void CreateUserNfcInterface(HLERequestContext& ctx) { LOG_DEBUG(Service_NFC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -197,7 +201,7 @@ public: explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} { // clang-format off static const FunctionInfo functions[] = { - {0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"}, + {0, &NFC_SYS::CreateSystemNfcInterface, "CreateSystemNfcInterface"}, }; // clang-format on @@ -205,7 +209,7 @@ public: } private: - void CreateSystemInterface(HLERequestContext& ctx) { + void CreateSystemNfcInterface(HLERequestContext& ctx) { LOG_DEBUG(Service_NFC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -221,6 +225,7 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("nfc:mf:u", std::make_shared(system)); server_manager->RegisterNamedService("nfc:user", std::make_shared(system)); server_manager->RegisterNamedService("nfc:sys", std::make_shared(system)); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp deleted file mode 100644 index 47bc1a05d..000000000 --- a/src/core/hle/service/nfc/nfc_device.cpp +++ /dev/null @@ -1,287 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/input.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfc/nfc_device.h" -#include "core/hle/service/nfc/nfc_result.h" - -namespace Service::NFC { -NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, - KernelHelpers::ServiceContext& service_context_, - Kernel::KEvent* availability_change_event_) - : npad_id{npad_id_}, system{system_}, service_context{service_context_}, - availability_change_event{availability_change_event_} { - activate_event = service_context.CreateEvent("IUser:NFCActivateEvent"); - deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent"); - npad_device = system.HIDCore().GetEmulatedController(npad_id); - - Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, - .is_npad_service = false, - }; - is_controller_set = true; - callback_key = npad_device->SetCallback(engine_callback); -} - -NfcDevice::~NfcDevice() { - activate_event->Close(); - deactivate_event->Close(); - if (!is_controller_set) { - return; - } - npad_device->DeleteCallback(callback_key); - is_controller_set = false; -}; - -void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { - if (!is_initalized) { - return; - } - - if (type == Core::HID::ControllerTriggerType::Connected) { - Initialize(); - availability_change_event->Signal(); - return; - } - - if (type == Core::HID::ControllerTriggerType::Disconnected) { - device_state = NFP::DeviceState::Unavailable; - availability_change_event->Signal(); - return; - } - - if (type != Core::HID::ControllerTriggerType::Nfc) { - return; - } - - if (!npad_device->IsConnected()) { - return; - } - - const auto nfc_status = npad_device->GetNfc(); - switch (nfc_status.state) { - case Common::Input::NfcState::NewAmiibo: - LoadNfcTag(nfc_status.data); - break; - case Common::Input::NfcState::AmiiboRemoved: - if (device_state != NFP::DeviceState::SearchingForTag) { - CloseNfcTag(); - } - break; - default: - break; - } -} - -bool NfcDevice::LoadNfcTag(std::span data) { - if (device_state != NFP::DeviceState::SearchingForTag) { - LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state); - return false; - } - - if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { - LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); - return false; - } - - tag_data.resize(data.size()); - memcpy(tag_data.data(), data.data(), data.size()); - memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); - - device_state = NFP::DeviceState::TagFound; - deactivate_event->GetReadableEvent().Clear(); - activate_event->Signal(); - return true; -} - -void NfcDevice::CloseNfcTag() { - LOG_INFO(Service_NFC, "Remove nfc tag"); - - device_state = NFP::DeviceState::TagRemoved; - encrypted_tag_data = {}; - activate_event->GetReadableEvent().Clear(); - deactivate_event->Signal(); -} - -Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const { - return activate_event->GetReadableEvent(); -} - -Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const { - return deactivate_event->GetReadableEvent(); -} - -void NfcDevice::Initialize() { - device_state = - npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable; - encrypted_tag_data = {}; - is_initalized = true; -} - -void NfcDevice::Finalize() { - if (device_state == NFP::DeviceState::SearchingForTag || - device_state == NFP::DeviceState::TagRemoved) { - StopDetection(); - } - device_state = NFP::DeviceState::Unavailable; - is_initalized = false; -} - -Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) { - if (device_state != NFP::DeviceState::Initialized && - device_state != NFP::DeviceState::TagRemoved) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - return WrongDeviceState; - } - - if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::NFC) != - Common::Input::DriverResult::Success) { - LOG_ERROR(Service_NFC, "Nfc not supported"); - return NfcDisabled; - } - - device_state = NFP::DeviceState::SearchingForTag; - allowed_protocols = allowed_protocol; - return ResultSuccess; -} - -Result NfcDevice::StopDetection() { - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active); - - if (device_state == NFP::DeviceState::Initialized) { - return ResultSuccess; - } - - if (device_state == NFP::DeviceState::TagFound || - device_state == NFP::DeviceState::TagMounted) { - CloseNfcTag(); - return ResultSuccess; - } - if (device_state == NFP::DeviceState::SearchingForTag || - device_state == NFP::DeviceState::TagRemoved) { - device_state = NFP::DeviceState::Initialized; - return ResultSuccess; - } - - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - return WrongDeviceState; -} - -Result NfcDevice::Flush() { - if (device_state != NFP::DeviceState::TagFound && - device_state != NFP::DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == NFP::DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (!npad_device->WriteNfc(tag_data)) { - LOG_ERROR(Service_NFP, "Error writing to file"); - return MifareReadError; - } - - return ResultSuccess; -} - -Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { - if (device_state != NFP::DeviceState::TagFound && - device_state != NFP::DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == NFP::DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (is_mifare) { - tag_info = { - .uuid = encrypted_tag_data.uuid.uid, - .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), - .protocol = NFP::TagProtocol::TypeA, - .tag_type = NFP::TagType::Type4, - }; - return ResultSuccess; - } - - // Protocol and tag type may change here - tag_info = { - .uuid = encrypted_tag_data.uuid.uid, - .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), - .protocol = NFP::TagProtocol::TypeA, - .tag_type = NFP::TagType::Type2, - }; - - return ResultSuccess; -} - -Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter, - NFP::MifareReadBlockData& read_block_data) { - const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock); - read_block_data.sector_number = parameter.sector_number; - - if (device_state != NFP::DeviceState::TagFound && - device_state != NFP::DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == NFP::DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) { - return MifareReadError; - } - - // TODO: Use parameter.sector_key to read encrypted data - memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock)); - - return ResultSuccess; -} - -Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) { - const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock); - - if (device_state != NFP::DeviceState::TagFound && - device_state != NFP::DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == NFP::DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) { - return MifareReadError; - } - - // TODO: Use parameter.sector_key to encrypt the data - memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock)); - - return ResultSuccess; -} - -u64 NfcDevice::GetHandle() const { - // Generate a handle based of the npad id - return static_cast(npad_id); -} - -NFP::DeviceState NfcDevice::GetCurrentState() const { - return device_state; -} - -Core::HID::NpadIdType NfcDevice::GetNpadId() const { - return npad_id; -} - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_device.h b/src/core/hle/service/nfc/nfc_device.h deleted file mode 100644 index ea63f0537..000000000 --- a/src/core/hle/service/nfc/nfc_device.h +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nfp/nfp_types.h" -#include "core/hle/service/service.h" - -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Core { -class System; -} // namespace Core - -namespace Core::HID { -class EmulatedController; -enum class ControllerTriggerType; -enum class NpadIdType : u32; -} // namespace Core::HID - -namespace Service::NFC { -class NfcDevice { -public: - NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, - KernelHelpers::ServiceContext& service_context_, - Kernel::KEvent* availability_change_event_); - ~NfcDevice(); - - void Initialize(); - void Finalize(); - - Result StartDetection(NFP::TagProtocol allowed_protocol); - Result StopDetection(); - Result Flush(); - - Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const; - - Result MifareRead(const NFP::MifareReadBlockParameter& parameter, - NFP::MifareReadBlockData& read_block_data); - - Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter); - - u64 GetHandle() const; - NFP::DeviceState GetCurrentState() const; - Core::HID::NpadIdType GetNpadId() const; - - Kernel::KReadableEvent& GetActivateEvent() const; - Kernel::KReadableEvent& GetDeactivateEvent() const; - -private: - void NpadUpdate(Core::HID::ControllerTriggerType type); - bool LoadNfcTag(std::span data); - void CloseNfcTag(); - - bool is_controller_set{}; - int callback_key; - const Core::HID::NpadIdType npad_id; - Core::System& system; - Core::HID::EmulatedController* npad_device = nullptr; - KernelHelpers::ServiceContext& service_context; - Kernel::KEvent* activate_event = nullptr; - Kernel::KEvent* deactivate_event = nullptr; - Kernel::KEvent* availability_change_event = nullptr; - - bool is_initalized{}; - NFP::TagProtocol allowed_protocols{}; - NFP::DeviceState device_state{NFP::DeviceState::Unavailable}; - - NFP::EncryptedNTAG215File encrypted_tag_data{}; - std::vector tag_data{}; -}; - -} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index be96d0cbc..0fa29d398 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -6,55 +6,56 @@ #include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfc/nfc_device.h" +#include "core/hle/service/nfc/common/device.h" +#include "core/hle/service/nfc/common/device_manager.h" +#include "core/hle/service/nfc/mifare_result.h" +#include "core/hle/service/nfc/mifare_types.h" #include "core/hle/service/nfc/nfc_interface.h" #include "core/hle/service/nfc/nfc_result.h" +#include "core/hle/service/nfc/nfc_types.h" +#include "core/hle/service/nfp/nfp_result.h" #include "core/hle/service/time/clock_types.h" namespace Service::NFC { -Interface::Interface(Core::System& system_, const char* name) - : ServiceFramework{system_, name}, service_context{system_, service_name} { - availability_change_event = service_context.CreateEvent("Interface:AvailabilityChangeEvent"); +NfcInterface::NfcInterface(Core::System& system_, const char* name, BackendType service_backend) + : ServiceFramework{system_, name}, service_context{system_, service_name}, + backend_type{service_backend} {} - for (u32 device_index = 0; device_index < 10; device_index++) { - devices[device_index] = - std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, - service_context, availability_change_event); - } -} +NfcInterface ::~NfcInterface() = default; -Interface ::~Interface() { - availability_change_event->Close(); -} - -void Interface::Initialize(HLERequestContext& ctx) { +void NfcInterface::Initialize(HLERequestContext& ctx) { LOG_INFO(Service_NFC, "called"); - state = State::Initialized; + auto manager = GetManager(); + auto result = manager->Initialize(); - for (auto& device : devices) { - device->Initialize(); + if (result.IsSuccess()) { + state = State::Initialized; + } else { + manager->Finalize(); } IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); + rb.Push(result); } -void Interface::Finalize(HLERequestContext& ctx) { +void NfcInterface::Finalize(HLERequestContext& ctx) { LOG_INFO(Service_NFC, "called"); - state = State::NonInitialized; - - for (auto& device : devices) { - device->Finalize(); + if (state != State::NonInitialized) { + if (GetBackendType() != BackendType::None) { + GetManager()->Finalize(); + } + device_manager = nullptr; + state = State::NonInitialized; } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } -void Interface::GetState(HLERequestContext& ctx) { +void NfcInterface::GetState(HLERequestContext& ctx) { LOG_DEBUG(Service_NFC, "called"); IPC::ResponseBuilder rb{ctx, 3}; @@ -62,50 +63,28 @@ void Interface::GetState(HLERequestContext& ctx) { rb.PushEnum(state); } -void Interface::IsNfcEnabled(HLERequestContext& ctx) { +void NfcInterface::IsNfcEnabled(HLERequestContext& ctx) { LOG_DEBUG(Service_NFC, "called"); + // TODO: This calls nn::settings::detail::GetNfcEnableFlag + const bool is_enabled = true; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(state != State::NonInitialized); + rb.Push(is_enabled); } -void Interface::ListDevices(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - if (!ctx.CanWriteBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - if (ctx.GetWriteBufferSize() == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - +void NfcInterface::ListDevices(HLERequestContext& ctx) { std::vector nfp_devices; const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); + LOG_DEBUG(Service_NFC, "called"); - for (auto& device : devices) { - if (nfp_devices.size() >= max_allowed_devices) { - continue; - } - if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { - nfp_devices.push_back(device->GetHandle()); - } - } + auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices); + result = TranslateResultToServiceError(result); - if (nfp_devices.empty()) { + if (result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); + rb.Push(result); return; } @@ -116,210 +95,177 @@ void Interface::ListDevices(HLERequestContext& ctx) { rb.Push(static_cast(nfp_devices.size())); } -void Interface::GetDeviceState(HLERequestContext& ctx) { +void NfcInterface::GetDeviceState(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - auto device = GetNfcDevice(device_handle); + const auto device_state = GetManager()->GetDeviceState(device_handle); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (device_state > DeviceState::Finalized) { + ASSERT_MSG(false, "Invalid device state"); } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetCurrentState()); + rb.PushEnum(device_state); } -void Interface::GetNpadId(HLERequestContext& ctx) { +void NfcInterface::GetNpadId(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); + Core::HID::NpadIdType npad_id{}; + auto result = GetManager()->GetNpadId(device_handle, npad_id); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { + if (result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); + rb.Push(result); return; } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetNpadId()); + rb.PushEnum(npad_id); } -void Interface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) { +void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) { LOG_INFO(Service_NFC, "called"); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); + rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent()); } -void Interface::StartDetection(HLERequestContext& ctx) { +void NfcInterface::StartDetection(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - const auto nfp_protocol{rp.PopEnum()}; - LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + const auto tag_protocol{rp.PopEnum()}; + LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); + auto result = GetManager()->StartDetection(device_handle, tag_protocol); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->StartDetection(nfp_protocol); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } -void Interface::StopDetection(HLERequestContext& ctx) { +void NfcInterface::StopDetection(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + auto result = GetManager()->StopDetection(device_handle); + result = TranslateResultToServiceError(result); - const auto result = device.value()->StopDetection(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } -void Interface::GetTagInfo(HLERequestContext& ctx) { +void NfcInterface::GetTagInfo(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + TagInfo tag_info{}; + auto result = + GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare); + result = TranslateResultToServiceError(result); - auto device = GetNfcDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(tag_info); } - NFP::TagInfo tag_info{}; - const auto result = device.value()->GetTagInfo(tag_info, false); - ctx.WriteBuffer(tag_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } -void Interface::AttachActivateEvent(HLERequestContext& ctx) { +void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(GetManager()->AttachActivateEvent(device_handle)); +} - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } +void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetActivateEvent()); + rb.PushCopyObjects(GetManager()->AttachDeactivateEvent(device_handle)); } -void Interface::AttachDeactivateEvent(HLERequestContext& ctx) { +void NfcInterface::ReadMifare(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements()}; + std::vector read_commands(number_of_commands); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + memcpy(read_commands.data(), buffer.data(), + number_of_commands * sizeof(MifareReadBlockParameter)); - auto device = GetNfcDevice(device_handle); + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", + device_handle, number_of_commands); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + std::vector out_data(number_of_commands); + auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data); + result = TranslateResultToServiceError(result); + + if (result.IsSuccess()) { + ctx.WriteBuffer(out_data); } - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetDeactivateEvent()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void NfcInterface::WriteMifare(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements()}; + std::vector write_commands(number_of_commands); + + memcpy(write_commands.data(), buffer.data(), + number_of_commands * sizeof(MifareWriteBlockParameter)); + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}", + device_handle, number_of_commands); + + auto result = GetManager()->WriteMifare(device_handle, write_commands); + result = TranslateResultToServiceError(result); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } -void Interface::SendCommandByPassThrough(HLERequestContext& ctx) { +void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; const auto timeout{rp.PopRaw()}; const auto command_data{ctx.ReadBuffer()}; - LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", device_handle, timeout.ToSeconds(), command_data.size()); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfcDevice(device_handle); + std::vector out_data(1); + auto result = + GetManager()->SendCommandByPassThrough(device_handle, timeout, command_data, out_data); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { + if (result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); + rb.Push(result); return; } - std::vector out_data(1); - // TODO: Request data from nfc device ctx.WriteBuffer(out_data); IPC::ResponseBuilder rb{ctx, 3}; @@ -327,13 +273,110 @@ void Interface::SendCommandByPassThrough(HLERequestContext& ctx) { rb.Push(static_cast(out_data.size())); } -std::optional> Interface::GetNfcDevice(u64 handle) { - for (auto& device : devices) { - if (device->GetHandle() == handle) { - return device; +std::shared_ptr NfcInterface::GetManager() { + if (device_manager == nullptr) { + device_manager = std::make_shared(system, service_context); + } + return device_manager; +} + +BackendType NfcInterface::GetBackendType() const { + return backend_type; +} + +Result NfcInterface::TranslateResultToServiceError(Result result) const { + const auto backend = GetBackendType(); + + if (result.IsSuccess()) { + return result; + } + + if (result.module != ErrorModule::NFC) { + return result; + } + + switch (backend) { + case BackendType::Mifare: + return TranslateResultToNfp(result); + case BackendType::Nfp: { + return TranslateResultToNfp(result); + } + default: + if (result != ResultUnknown216) { + return result; } + return ResultUnknown74; + } +} + +Result NfcInterface::TranslateResultToNfp(Result result) const { + if (result == ResultDeviceNotFound) { + return NFP::ResultDeviceNotFound; + } + if (result == ResultInvalidArgument) { + return NFP::ResultInvalidArgument; + } + if (result == ResultWrongApplicationAreaSize) { + return NFP::ResultWrongApplicationAreaSize; + } + if (result == ResultWrongDeviceState) { + return NFP::ResultWrongDeviceState; + } + if (result == ResultUnknown74) { + return NFP::ResultUnknown74; + } + if (result == ResultNfcDisabled) { + return NFP::ResultNfcDisabled; + } + if (result == ResultNfcNotInitialized) { + return NFP::ResultNfcDisabled; + } + if (result == ResultWriteAmiiboFailed) { + return NFP::ResultWriteAmiiboFailed; + } + if (result == ResultTagRemoved) { + return NFP::ResultTagRemoved; + } + if (result == ResultRegistrationIsNotInitialized) { + return NFP::ResultRegistrationIsNotInitialized; + } + if (result == ResultApplicationAreaIsNotInitialized) { + return NFP::ResultApplicationAreaIsNotInitialized; + } + if (result == ResultCorruptedData) { + return NFP::ResultCorruptedData; + } + if (result == ResultWrongApplicationAreaId) { + return NFP::ResultWrongApplicationAreaId; + } + if (result == ResultApplicationAreaExist) { + return NFP::ResultApplicationAreaExist; + } + if (result == ResultNotAnAmiibo) { + return NFP::ResultNotAnAmiibo; + } + LOG_WARNING(Service_NFC, "Result conversion not handled"); + return result; +} + +Result NfcInterface::TranslateResultToMifare(Result result) const { + if (result == ResultDeviceNotFound) { + return Mifare::ResultDeviceNotFound; + } + if (result == ResultInvalidArgument) { + return Mifare::ResultInvalidArgument; + } + if (result == ResultWrongDeviceState) { + return Mifare::ResultWrongDeviceState; + } + if (result == ResultNfcDisabled) { + return Mifare::ResultNfcDisabled; + } + if (result == ResultTagRemoved) { + return Mifare::ResultTagRemoved; } - return std::nullopt; + LOG_WARNING(Service_NFC, "Result conversion not handled"); + return result; } } // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_interface.h b/src/core/hle/service/nfc/nfc_interface.h index 8c1bf5a59..08be174d8 100644 --- a/src/core/hle/service/nfc/nfc_interface.h +++ b/src/core/hle/service/nfc/nfc_interface.h @@ -3,20 +3,17 @@ #pragma once -#include -#include -#include - #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/service.h" namespace Service::NFC { -class NfcDevice; +class DeviceManager; -class Interface : public ServiceFramework { +class NfcInterface : public ServiceFramework { public: - explicit Interface(Core::System& system_, const char* name); - ~Interface(); + explicit NfcInterface(Core::System& system_, const char* name, BackendType service_backend); + ~NfcInterface(); void Initialize(HLERequestContext& ctx); void Finalize(HLERequestContext& ctx); @@ -31,22 +28,22 @@ public: void GetTagInfo(HLERequestContext& ctx); void AttachActivateEvent(HLERequestContext& ctx); void AttachDeactivateEvent(HLERequestContext& ctx); + void ReadMifare(HLERequestContext& ctx); + void WriteMifare(HLERequestContext& ctx); void SendCommandByPassThrough(HLERequestContext& ctx); -private: - enum class State : u32 { - NonInitialized, - Initialized, - }; - - std::optional> GetNfcDevice(u64 handle); +protected: + std::shared_ptr GetManager(); + BackendType GetBackendType() const; + Result TranslateResultToServiceError(Result result) const; + Result TranslateResultToNfp(Result result) const; + Result TranslateResultToMifare(Result result) const; KernelHelpers::ServiceContext service_context; - std::array, 10> devices{}; - + BackendType backend_type; State state{State::NonInitialized}; - Kernel::KEvent* availability_change_event; + std::shared_ptr device_manager = nullptr; }; } // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 146b8ba61..917d79ef8 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -7,17 +7,22 @@ namespace Service::NFC { -constexpr Result DeviceNotFound(ErrorModule::NFC, 64); -constexpr Result InvalidArgument(ErrorModule::NFC, 65); -constexpr Result WrongDeviceState(ErrorModule::NFC, 73); -constexpr Result NfcDisabled(ErrorModule::NFC, 80); -constexpr Result TagRemoved(ErrorModule::NFC, 97); - -constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64); -constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65); -constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73); -constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80); -constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97); -constexpr Result MifareReadError(ErrorModule::NFCMifare, 288); +constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64); +constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65); +constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68); +constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73); +constexpr Result ResultUnknown74(ErrorModule::NFC, 74); +constexpr Result ResultUnknown76(ErrorModule::NFC, 76); +constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77); +constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); +constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); +constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); +constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); +constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); +constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); +constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); +constexpr Result ResultUnknown216(ErrorModule::NFC, 216); } // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_types.h b/src/core/hle/service/nfc/nfc_types.h new file mode 100644 index 000000000..c7ebd1fdb --- /dev/null +++ b/src/core/hle/service/nfc/nfc_types.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::NFC { +enum class BackendType : u32 { + None, + Nfc, + Nfp, + Mifare, +}; + +// This is nn::nfc::DeviceState +enum class DeviceState : u32 { + Initialized, + SearchingForTag, + TagFound, + TagRemoved, + TagMounted, + Unavailable, + Finalized, +}; + +// This is nn::nfc::State +enum class State : u32 { + NonInitialized, + Initialized, +}; + +// This is nn::nfc::TagType +enum class TagType : u32 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +enum class PackedTagType : u8 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +// This is nn::nfc::NfcProtocol +// Verify this enum. It might be completely wrong default protocol is 0x48 +enum class NfcProtocol : u32 { + None, + TypeA = 1U << 0, // ISO14443A + TypeB = 1U << 1, // ISO14443B + TypeF = 1U << 2, // Sony FeliCa + Unknown1 = 1U << 3, + Unknown2 = 1U << 5, + All = 0xFFFFFFFFU, +}; + +// this is nn::nfc::TestWaveType +enum class TestWaveType : u32 { + Unknown, +}; + +using UniqueSerialNumber = std::array; +using UniqueSerialNumberExtension = std::array; + +// This is nn::nfc::DeviceHandle +using DeviceHandle = u64; + +// This is nn::nfc::TagInfo +struct TagInfo { + UniqueSerialNumber uuid; + UniqueSerialNumberExtension uuid_extension; + u8 uuid_length; + INSERT_PADDING_BYTES(0x15); + NfcProtocol protocol; + TagType tag_type; + INSERT_PADDING_BYTES(0x30); +}; +static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp deleted file mode 100644 index a3622e792..000000000 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ /dev/null @@ -1,405 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2017 socram8888/amiitool -// SPDX-License-Identifier: MIT - -#include -#include -#include - -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "core/hle/service/nfp/amiibo_crypto.h" - -namespace Service::NFP::AmiiboCrypto { - -bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { - const auto& amiibo_data = ntag_file.user_memory; - LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); - LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); - LOG_DEBUG(Service_NFP, "write_count={}", static_cast(amiibo_data.write_counter)); - - LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); - LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); - LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); - LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", - static_cast(amiibo_data.model_info.model_number)); - LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); - LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); - - LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); - LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); - LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", ntag_file.CFG1); - - // Validate UUID - constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` - if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != - ntag_file.uuid.uid[3]) { - return false; - } - if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ - ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { - return false; - } - - // Check against all know constants on an amiibo binary - if (ntag_file.static_lock != 0xE00F) { - return false; - } - if (ntag_file.compability_container != 0xEEFF10F1U) { - return false; - } - if (amiibo_data.constant_value != 0xA5) { - return false; - } - if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { - return false; - } - if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { - return false; - } - if (ntag_file.CFG0 != 0x04000000U) { - return false; - } - if (ntag_file.CFG1 != 0x5F) { - return false; - } - return true; -} - -bool IsAmiiboValid(const NTAG215File& ntag_file) { - return IsAmiiboValid(EncodedDataToNfcData(ntag_file)); -} - -NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { - NTAG215File encoded_data{}; - - encoded_data.uid = nfc_data.uuid.uid; - encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; - encoded_data.static_lock = nfc_data.static_lock; - encoded_data.compability_container = nfc_data.compability_container; - encoded_data.hmac_data = nfc_data.user_memory.hmac_data; - encoded_data.constant_value = nfc_data.user_memory.constant_value; - encoded_data.write_counter = nfc_data.user_memory.write_counter; - encoded_data.amiibo_version = nfc_data.user_memory.amiibo_version; - encoded_data.settings = nfc_data.user_memory.settings; - encoded_data.owner_mii = nfc_data.user_memory.owner_mii; - encoded_data.application_id = nfc_data.user_memory.application_id; - encoded_data.application_write_counter = nfc_data.user_memory.application_write_counter; - encoded_data.application_area_id = nfc_data.user_memory.application_area_id; - encoded_data.application_id_byte = nfc_data.user_memory.application_id_byte; - encoded_data.unknown = nfc_data.user_memory.unknown; - encoded_data.mii_extension = nfc_data.user_memory.mii_extension; - encoded_data.unknown2 = nfc_data.user_memory.unknown2; - encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc; - encoded_data.application_area = nfc_data.user_memory.application_area; - encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; - encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; - encoded_data.model_info = nfc_data.user_memory.model_info; - encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; - encoded_data.dynamic_lock = nfc_data.dynamic_lock; - encoded_data.CFG0 = nfc_data.CFG0; - encoded_data.CFG1 = nfc_data.CFG1; - encoded_data.password = nfc_data.password; - - return encoded_data; -} - -EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { - EncryptedNTAG215File nfc_data{}; - - nfc_data.uuid.uid = encoded_data.uid; - nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; - nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; - nfc_data.static_lock = encoded_data.static_lock; - nfc_data.compability_container = encoded_data.compability_container; - nfc_data.user_memory.hmac_data = encoded_data.hmac_data; - nfc_data.user_memory.constant_value = encoded_data.constant_value; - nfc_data.user_memory.write_counter = encoded_data.write_counter; - nfc_data.user_memory.amiibo_version = encoded_data.amiibo_version; - nfc_data.user_memory.settings = encoded_data.settings; - nfc_data.user_memory.owner_mii = encoded_data.owner_mii; - nfc_data.user_memory.application_id = encoded_data.application_id; - nfc_data.user_memory.application_write_counter = encoded_data.application_write_counter; - nfc_data.user_memory.application_area_id = encoded_data.application_area_id; - nfc_data.user_memory.application_id_byte = encoded_data.application_id_byte; - nfc_data.user_memory.unknown = encoded_data.unknown; - nfc_data.user_memory.mii_extension = encoded_data.mii_extension; - nfc_data.user_memory.unknown2 = encoded_data.unknown2; - nfc_data.user_memory.register_info_crc = encoded_data.register_info_crc; - nfc_data.user_memory.application_area = encoded_data.application_area; - nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; - nfc_data.user_memory.model_info = encoded_data.model_info; - nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt; - nfc_data.dynamic_lock = encoded_data.dynamic_lock; - nfc_data.CFG0 = encoded_data.CFG0; - nfc_data.CFG1 = encoded_data.CFG1; - nfc_data.password = encoded_data.password; - - return nfc_data; -} - -u32 GetTagPassword(const TagUuid& uuid) { - // Verify that the generated password is correct - u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); - password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; - password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; - password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; - return password; -} - -HashSeed GetSeed(const NTAG215File& data) { - HashSeed seed{ - .magic = data.write_counter, - .padding = {}, - .uid_1 = data.uid, - .nintendo_id_1 = data.nintendo_id, - .uid_2 = data.uid, - .nintendo_id_2 = data.nintendo_id, - .keygen_salt = data.keygen_salt, - }; - - return seed; -} - -std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed) { - const std::size_t seedPart1Len = sizeof(key.magic_bytes) - key.magic_length; - const std::size_t string_size = key.type_string.size(); - std::vector output(string_size + seedPart1Len); - - // Copy whole type string - memccpy(output.data(), key.type_string.data(), '\0', string_size); - - // Append (16 - magic_length) from the input seed - memcpy(output.data() + string_size, &seed, seedPart1Len); - - // Append all bytes from magicBytes - output.insert(output.end(), key.magic_bytes.begin(), - key.magic_bytes.begin() + key.magic_length); - - output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); - output.emplace_back(seed.nintendo_id_1); - output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); - output.emplace_back(seed.nintendo_id_2); - - for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { - output.emplace_back(static_cast(seed.keygen_salt[i] ^ key.xor_pad[i])); - } - - return output; -} - -void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, - const std::vector& seed) { - // Initialize context - ctx.used = false; - ctx.counter = 0; - ctx.buffer_size = sizeof(ctx.counter) + seed.size(); - memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size()); - - // Initialize HMAC context - mbedtls_md_init(&hmac_ctx); - mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); - mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size()); -} - -void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) { - // If used at least once, reinitialize the HMAC - if (ctx.used) { - mbedtls_md_hmac_reset(&hmac_ctx); - } - - ctx.used = true; - - // Store counter in big endian, and increment it - ctx.buffer[0] = static_cast(ctx.counter >> 8); - ctx.buffer[1] = static_cast(ctx.counter >> 0); - ctx.counter++; - - // Do HMAC magic - mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast(ctx.buffer.data()), - ctx.buffer_size); - mbedtls_md_hmac_finish(&hmac_ctx, output.data()); -} - -DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) { - const auto seed = GetSeed(data); - - // Generate internal seed - const std::vector internal_key = GenerateInternalKey(key, seed); - - // Initialize context - CryptoCtx ctx{}; - mbedtls_md_context_t hmac_ctx; - CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key); - - // Generate derived keys - DerivedKeys derived_keys{}; - std::array temp{}; - CryptoStep(ctx, hmac_ctx, temp[0]); - CryptoStep(ctx, hmac_ctx, temp[1]); - memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys)); - - // Cleanup context - mbedtls_md_free(&hmac_ctx); - - return derived_keys; -} - -void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) { - mbedtls_aes_context aes; - std::size_t nc_off = 0; - std::array nonce_counter{}; - std::array stream_block{}; - - const auto aes_key_size = static_cast(keys.aes_key.size() * 8); - mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), aes_key_size); - memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(keys.aes_iv)); - - constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START; - mbedtls_aes_crypt_ctr(&aes, encrypted_data_size, &nc_off, nonce_counter.data(), - stream_block.data(), - reinterpret_cast(&in_data.settings), - reinterpret_cast(&out_data.settings)); - - // Copy the rest of the data directly - out_data.uid = in_data.uid; - out_data.nintendo_id = in_data.nintendo_id; - out_data.lock_bytes = in_data.lock_bytes; - out_data.static_lock = in_data.static_lock; - out_data.compability_container = in_data.compability_container; - - out_data.constant_value = in_data.constant_value; - out_data.write_counter = in_data.write_counter; - - out_data.model_info = in_data.model_info; - out_data.keygen_salt = in_data.keygen_salt; - out_data.dynamic_lock = in_data.dynamic_lock; - out_data.CFG0 = in_data.CFG0; - out_data.CFG1 = in_data.CFG1; - out_data.password = in_data.password; -} - -bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { - const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); - - const Common::FS::IOFile keys_file{yuzu_keys_dir / "key_retail.bin", - Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; - - if (!keys_file.IsOpen()) { - LOG_ERROR(Service_NFP, "Failed to open key file"); - return false; - } - - if (keys_file.Read(unfixed_info) != 1) { - LOG_ERROR(Service_NFP, "Failed to read unfixed_info"); - return false; - } - if (keys_file.Read(locked_secret) != 1) { - LOG_ERROR(Service_NFP, "Failed to read locked-secret"); - return false; - } - - return true; -} - -bool IsKeyAvailable() { - const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); - return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin"); -} - -bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { - InternalKey locked_secret{}; - InternalKey unfixed_info{}; - - if (!LoadKeys(locked_secret, unfixed_info)) { - return false; - } - - // Generate keys - NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data); - const auto data_keys = GenerateKey(unfixed_info, encoded_data); - const auto tag_keys = GenerateKey(locked_secret, encoded_data); - - // Decrypt - Cipher(data_keys, encoded_data, tag_data); - - // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! - constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; - mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), - sizeof(HmacKey), reinterpret_cast(&tag_data.uid), - input_length, reinterpret_cast(&tag_data.hmac_tag)); - - // Regenerate data HMAC - constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START; - mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(), - sizeof(HmacKey), - reinterpret_cast(&tag_data.write_counter), input_length2, - reinterpret_cast(&tag_data.hmac_data)); - - if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) { - LOG_ERROR(Service_NFP, "hmac_data doesn't match"); - return false; - } - - if (tag_data.hmac_tag != encrypted_tag_data.user_memory.hmac_tag) { - LOG_ERROR(Service_NFP, "hmac_tag doesn't match"); - return false; - } - - return true; -} - -bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) { - InternalKey locked_secret{}; - InternalKey unfixed_info{}; - - if (!LoadKeys(locked_secret, unfixed_info)) { - return false; - } - - // Generate keys - const auto data_keys = GenerateKey(unfixed_info, tag_data); - const auto tag_keys = GenerateKey(locked_secret, tag_data); - - NTAG215File encoded_tag_data{}; - - // Generate tag HMAC - constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; - constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; - mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), - sizeof(HmacKey), reinterpret_cast(&tag_data.uid), - input_length, reinterpret_cast(&encoded_tag_data.hmac_tag)); - - // Init mbedtls HMAC context - mbedtls_md_context_t ctx; - mbedtls_md_init(&ctx); - mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); - - // Generate data HMAC - mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey)); - mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.write_counter), - input_length2); // Data - mbedtls_md_hmac_update(&ctx, reinterpret_cast(&encoded_tag_data.hmac_tag), - sizeof(HashData)); // Tag HMAC - mbedtls_md_hmac_update(&ctx, reinterpret_cast(&tag_data.uid), - input_length); - mbedtls_md_hmac_finish(&ctx, reinterpret_cast(&encoded_tag_data.hmac_data)); - - // HMAC cleanup - mbedtls_md_free(&ctx); - - // Encrypt - Cipher(data_keys, tag_data, encoded_tag_data); - - // Convert back to hardware - encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data); - - return true; -} - -} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h deleted file mode 100644 index f6208ee6b..000000000 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include "core/hle/service/nfp/nfp_types.h" - -struct mbedtls_md_context_t; - -namespace Service::NFP::AmiiboCrypto { -// Byte locations in Service::NFP::NTAG215File -constexpr std::size_t HMAC_DATA_START = 0x8; -constexpr std::size_t SETTINGS_START = 0x2c; -constexpr std::size_t WRITE_COUNTER_START = 0x29; -constexpr std::size_t HMAC_TAG_START = 0x1B4; -constexpr std::size_t UUID_START = 0x1D4; -constexpr std::size_t DYNAMIC_LOCK_START = 0x208; - -using HmacKey = std::array; -using DrgbOutput = std::array; - -struct HashSeed { - u16_be magic; - std::array padding; - UniqueSerialNumber uid_1; - u8 nintendo_id_1; - UniqueSerialNumber uid_2; - u8 nintendo_id_2; - std::array keygen_salt; -}; -static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); - -struct InternalKey { - HmacKey hmac_key; - std::array type_string; - u8 reserved; - u8 magic_length; - std::array magic_bytes; - std::array xor_pad; -}; -static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size"); -static_assert(std::is_trivially_copyable_v, "InternalKey must be trivially copyable."); - -struct CryptoCtx { - std::array buffer; - bool used; - std::size_t buffer_size; - s16 counter; -}; - -struct DerivedKeys { - std::array aes_key; - std::array aes_iv; - std::array hmac_key; -}; -static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); - -/// Validates that the amiibo file is not corrupted -bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file); - -/// Validates that the amiibo file is not corrupted -bool IsAmiiboValid(const NTAG215File& ntag_file); - -/// Converts from encrypted file format to encoded file format -NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data); - -/// Converts from encoded file format to encrypted file format -EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); - -/// Returns password needed to allow write access to protected memory -u32 GetTagPassword(const TagUuid& uuid); - -// Generates Seed needed for key derivation -HashSeed GetSeed(const NTAG215File& data); - -// Middle step on the generation of derived keys -std::vector GenerateInternalKey(const InternalKey& key, const HashSeed& seed); - -// Initializes mbedtls context -void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, - const std::vector& seed); - -// Feeds data to mbedtls context to generate the derived key -void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output); - -// Generates the derived key from amiibo data -DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data); - -// Encodes or decodes amiibo data -void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data); - -/// Loads both amiibo keys from key_retail.bin -bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); - -/// Returns true if key_retail.bin exist -bool IsKeyAvailable(); - -/// Decodes encrypted amiibo data returns true if output is valid -bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); - -/// Encodes plain amiibo data returns true if output is valid -bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data); - -} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 2559fe598..2eeabc138 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -13,7 +13,7 @@ class IUser final : public Interface { public: explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") { // clang-format off - static const FunctionInfo functions[] = { + static const FunctionInfoTyped functions[] = { {0, &IUser::Initialize, "Initialize"}, {1, &IUser::Finalize, "Finalize"}, {2, &IUser::ListDevices, "ListDevices"}, @@ -50,7 +50,7 @@ class ISystem final : public Interface { public: explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") { // clang-format off - static const FunctionInfo functions[] = { + static const FunctionInfoTyped functions[] = { {0, &ISystem::InitializeSystem, "InitializeSystem"}, {1, &ISystem::FinalizeSystem, "FinalizeSystem"}, {2, &ISystem::ListDevices, "ListDevices"}, @@ -89,7 +89,7 @@ class IDebug final : public Interface { public: explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") { // clang-format off - static const FunctionInfo functions[] = { + static const FunctionInfoTyped functions[] = { {0, &IDebug::InitializeDebug, "InitializeDebug"}, {1, &IDebug::FinalizeDebug, "FinalizeDebug"}, {2, &IDebug::ListDevices, "ListDevices"}, @@ -126,9 +126,9 @@ public: {201, &IDebug::SetAll, "SetAll"}, {202, &IDebug::FlushDebug, "FlushDebug"}, {203, &IDebug::BreakTag, "BreakTag"}, - {204, nullptr, "ReadBackupData"}, - {205, nullptr, "WriteBackupData"}, - {206, nullptr, "WriteNtf"}, + {204, &IDebug::ReadBackupData, "ReadBackupData"}, + {205, &IDebug::WriteBackupData, "WriteBackupData"}, + {206, &IDebug::WriteNtf, "WriteNtf"}, }; // clang-format on diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp deleted file mode 100644 index 3f9af53c8..000000000 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ /dev/null @@ -1,1137 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used -#endif - -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "common/input.h" -#include "common/logging/log.h" -#include "common/string_util.h" -#include "common/tiny_mt.h" -#include "core/core.h" -#include "core/hid/emulated_controller.h" -#include "core/hid/hid_core.h" -#include "core/hid/hid_types.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/mii/mii_manager.h" -#include "core/hle/service/mii/types.h" -#include "core/hle/service/nfp/amiibo_crypto.h" -#include "core/hle/service/nfp/nfp_device.h" -#include "core/hle/service/nfp/nfp_result.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_manager.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::NFP { -NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, - KernelHelpers::ServiceContext& service_context_, - Kernel::KEvent* availability_change_event_) - : npad_id{npad_id_}, system{system_}, service_context{service_context_}, - availability_change_event{availability_change_event_} { - activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); - deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); - npad_device = system.HIDCore().GetEmulatedController(npad_id); - - Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, - .is_npad_service = false, - }; - is_controller_set = true; - callback_key = npad_device->SetCallback(engine_callback); - - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; -} - -NfpDevice::~NfpDevice() { - activate_event->Close(); - deactivate_event->Close(); - if (!is_controller_set) { - return; - } - npad_device->DeleteCallback(callback_key); - is_controller_set = false; -}; - -void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { - if (!is_initalized) { - return; - } - - if (type == Core::HID::ControllerTriggerType::Connected) { - Initialize(); - availability_change_event->Signal(); - return; - } - - if (type == Core::HID::ControllerTriggerType::Disconnected) { - device_state = DeviceState::Unavailable; - availability_change_event->Signal(); - return; - } - - if (type != Core::HID::ControllerTriggerType::Nfc) { - return; - } - - if (!npad_device->IsConnected()) { - return; - } - - const auto nfc_status = npad_device->GetNfc(); - switch (nfc_status.state) { - case Common::Input::NfcState::NewAmiibo: - LoadAmiibo(nfc_status.data); - break; - case Common::Input::NfcState::AmiiboRemoved: - if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { - break; - } - if (device_state != DeviceState::SearchingForTag) { - CloseAmiibo(); - } - break; - default: - break; - } -} - -bool NfpDevice::LoadAmiibo(std::span data) { - if (device_state != DeviceState::SearchingForTag) { - LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); - return false; - } - - if (data.size() != sizeof(EncryptedNTAG215File)) { - LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); - return false; - } - - // TODO: Filter by allowed_protocols here - - memcpy(&tag_data, data.data(), sizeof(EncryptedNTAG215File)); - is_plain_amiibo = AmiiboCrypto::IsAmiiboValid(tag_data); - - if (is_plain_amiibo) { - encrypted_tag_data = AmiiboCrypto::EncodedDataToNfcData(tag_data); - LOG_INFO(Service_NFP, "Using plain amiibo"); - } else { - tag_data = {}; - memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); - } - - device_state = DeviceState::TagFound; - deactivate_event->GetReadableEvent().Clear(); - activate_event->Signal(); - return true; -} - -void NfpDevice::CloseAmiibo() { - LOG_INFO(Service_NFP, "Remove amiibo"); - - if (device_state == DeviceState::TagMounted) { - Unmount(); - } - - device_state = DeviceState::TagRemoved; - encrypted_tag_data = {}; - tag_data = {}; - activate_event->GetReadableEvent().Clear(); - deactivate_event->Signal(); -} - -Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { - return activate_event->GetReadableEvent(); -} - -Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { - return deactivate_event->GetReadableEvent(); -} - -void NfpDevice::Initialize() { - device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; - encrypted_tag_data = {}; - tag_data = {}; - is_initalized = true; -} - -void NfpDevice::Finalize() { - if (device_state == DeviceState::TagMounted) { - Unmount(); - } - if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { - StopDetection(); - } - device_state = DeviceState::Unavailable; - is_initalized = false; -} - -Result NfpDevice::StartDetection(TagProtocol allowed_protocol) { - if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return WrongDeviceState; - } - - if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::NFC) != - Common::Input::DriverResult::Success) { - LOG_ERROR(Service_NFP, "Nfc not supported"); - return NfcDisabled; - } - - device_state = DeviceState::SearchingForTag; - allowed_protocols = allowed_protocol; - return ResultSuccess; -} - -Result NfpDevice::StopDetection() { - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active); - - if (device_state == DeviceState::Initialized) { - return ResultSuccess; - } - - if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { - CloseAmiibo(); - } - - if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { - device_state = DeviceState::Initialized; - return ResultSuccess; - } - - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return WrongDeviceState; -} - -Result NfpDevice::Flush() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - auto& settings = tag_data.settings; - - const auto& current_date = GetAmiiboDate(current_posix_time); - if (settings.write_date.raw_date != current_date.raw_date) { - settings.write_date = current_date; - UpdateSettingsCrc(); - } - - tag_data.write_counter++; - - FlushWithBreak(BreakType::Normal); - - is_data_moddified = false; - - return ResultSuccess; -} - -Result NfpDevice::FlushDebug() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - tag_data.write_counter++; - - FlushWithBreak(BreakType::Normal); - - is_data_moddified = false; - - return ResultSuccess; -} - -Result NfpDevice::FlushWithBreak(BreakType break_type) { - if (break_type != BreakType::Normal) { - LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type); - return WrongDeviceState; - } - - std::vector data(sizeof(EncryptedNTAG215File)); - if (is_plain_amiibo) { - memcpy(data.data(), &tag_data, sizeof(tag_data)); - } else { - if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { - LOG_ERROR(Service_NFP, "Failed to encode data"); - return WriteAmiiboFailed; - } - - memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); - } - - if (!npad_device->WriteNfc(data)) { - LOG_ERROR(Service_NFP, "Error writing to file"); - return WriteAmiiboFailed; - } - - return ResultSuccess; -} - -Result NfpDevice::Mount(MountTarget mount_target_) { - if (device_state != DeviceState::TagFound) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - return WrongDeviceState; - } - - // The loaded amiibo is not encrypted - if (is_plain_amiibo) { - device_state = DeviceState::TagMounted; - mount_target = mount_target_; - return ResultSuccess; - } - - if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { - LOG_ERROR(Service_NFP, "Not an amiibo"); - return NotAnAmiibo; - } - - // Mark amiibos as read only when keys are missing - if (!AmiiboCrypto::IsKeyAvailable()) { - LOG_ERROR(Service_NFP, "No keys detected"); - device_state = DeviceState::TagMounted; - mount_target = MountTarget::Rom; - return ResultSuccess; - } - - if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { - LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); - return CorruptedData; - } - - device_state = DeviceState::TagMounted; - mount_target = mount_target_; - return ResultSuccess; -} - -Result NfpDevice::Unmount() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - // Save data before unloading the amiibo - if (is_data_moddified) { - Flush(); - } - - device_state = DeviceState::TagFound; - mount_target = MountTarget::None; - is_app_area_open = false; - - return ResultSuccess; -} - -Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { - if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - tag_info = { - .uuid = encrypted_tag_data.uuid.uid, - .uuid_length = static_cast(encrypted_tag_data.uuid.uid.size()), - .protocol = TagProtocol::TypeA, - .tag_type = TagType::Type2, - }; - - return ResultSuccess; -} - -Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - const auto& settings = tag_data.settings; - - // TODO: Validate this data - common_info = { - .last_write_date = settings.write_date.GetWriteDate(), - .write_counter = tag_data.write_counter, - .version = tag_data.amiibo_version, - .application_area_size = sizeof(ApplicationArea), - }; - return ResultSuccess; -} - -Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - const auto& model_info_data = encrypted_tag_data.user_memory.model_info; - model_info = { - .character_id = model_info_data.character_id, - .character_variant = model_info_data.character_variant, - .amiibo_type = model_info_data.amiibo_type, - .model_number = model_info_data.model_number, - .series = model_info_data.series, - }; - return ResultSuccess; -} - -Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (tag_data.settings.settings.amiibo_initialized == 0) { - return RegistrationIsNotInitialized; - } - - Service::Mii::MiiManager manager; - const auto& settings = tag_data.settings; - - // TODO: Validate this data - register_info = { - .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), - .creation_date = settings.init_date.GetWriteDate(), - .amiibo_name = GetAmiiboName(settings), - .font_region = settings.settings.font_region, - }; - - return ResultSuccess; -} - -Result NfpDevice::GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (tag_data.settings.settings.amiibo_initialized == 0) { - return RegistrationIsNotInitialized; - } - - Service::Mii::MiiManager manager; - const auto& settings = tag_data.settings; - - // TODO: Validate and complete this data - register_info = { - .mii_store_data = {}, - .creation_date = settings.init_date.GetWriteDate(), - .amiibo_name = GetAmiiboName(settings), - .font_region = settings.settings.font_region, - }; - - return ResultSuccess; -} - -Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - u8 flags = static_cast(tag_data.settings.settings.raw >> 0x4); - if (tag_data.settings.settings.amiibo_initialized == 0) { - flags = flags & 0xfe; - } - - u64 application_id = 0; - u32 application_area_id = 0; - AppAreaVersion app_area_version = AppAreaVersion::NotSet; - if (tag_data.settings.settings.appdata_initialized != 0) { - application_id = tag_data.application_id; - app_area_version = - static_cast(application_id >> application_id_version_offset & 0xf); - - // Restore application id to original value - if (application_id >> 0x38 != 0) { - const u8 application_byte = tag_data.application_id_byte & 0xf; - application_id = RemoveVersionByte(application_id) | - (static_cast(application_byte) << application_id_version_offset); - } - - application_area_id = tag_data.application_area_id; - } - - // TODO: Validate this data - admin_info = { - .application_id = application_id, - .application_area_id = application_area_id, - .crc_change_counter = tag_data.settings.crc_counter, - .flags = flags, - .tag_type = PackedTagType::Type2, - .app_area_version = app_area_version, - }; - - return ResultSuccess; -} - -Result NfpDevice::DeleteRegisterInfo() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (tag_data.settings.settings.amiibo_initialized == 0) { - return RegistrationIsNotInitialized; - } - - Common::TinyMT rng{}; - rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); - rng.GenerateRandomBytes(&tag_data.settings.amiibo_name, sizeof(tag_data.settings.amiibo_name)); - rng.GenerateRandomBytes(&tag_data.unknown, sizeof(u8)); - rng.GenerateRandomBytes(&tag_data.unknown2[0], sizeof(u32)); - rng.GenerateRandomBytes(&tag_data.unknown2[1], sizeof(u32)); - rng.GenerateRandomBytes(&tag_data.register_info_crc, sizeof(u32)); - rng.GenerateRandomBytes(&tag_data.settings.init_date, sizeof(u32)); - tag_data.settings.settings.font_region.Assign(0); - tag_data.settings.settings.amiibo_initialized.Assign(0); - - return Flush(); -} - -Result NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - Service::Mii::MiiManager manager; - const auto mii = manager.BuildDefault(0); - auto& settings = tag_data.settings; - - if (tag_data.settings.settings.amiibo_initialized == 0) { - settings.init_date = GetAmiiboDate(current_posix_time); - settings.write_date.raw_date = 0; - } - - SetAmiiboName(settings, amiibo_name); - tag_data.owner_mii = manager.BuildFromStoreData(mii); - tag_data.mii_extension = manager.SetFromStoreData(mii); - tag_data.unknown = 0; - tag_data.unknown2 = {}; - settings.country_code_id = 0; - settings.settings.font_region.Assign(0); - settings.settings.amiibo_initialized.Assign(1); - - UpdateRegisterInfoCrc(); - - return Flush(); -} - -Result NfpDevice::RestoreAmiibo() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - // TODO: Load amiibo from backup on system - LOG_ERROR(Service_NFP, "Not Implemented"); - return ResultSuccess; -} - -Result NfpDevice::Format() { - auto result1 = DeleteApplicationArea(); - auto result2 = DeleteRegisterInfo(); - - if (result1.IsError()) { - return result1; - } - - if (result2.IsError()) { - return result2; - } - - return Flush(); -} - -Result NfpDevice::OpenApplicationArea(u32 access_id) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized.Value() == 0) { - LOG_WARNING(Service_NFP, "Application area is not initialized"); - return ApplicationAreaIsNotInitialized; - } - - if (tag_data.application_area_id != access_id) { - LOG_WARNING(Service_NFP, "Wrong application area id"); - return WrongApplicationAreaId; - } - - is_app_area_open = true; - - return ResultSuccess; -} - -Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const { - application_area_id = {}; - - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized.Value() == 0) { - LOG_WARNING(Service_NFP, "Application area is not initialized"); - return ApplicationAreaIsNotInitialized; - } - - application_area_id = tag_data.application_area_id; - - return ResultSuccess; -} - -Result NfpDevice::GetApplicationArea(std::vector& data) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (!is_app_area_open) { - LOG_ERROR(Service_NFP, "Application area is not open"); - return WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized.Value() == 0) { - LOG_ERROR(Service_NFP, "Application area is not initialized"); - return ApplicationAreaIsNotInitialized; - } - - if (data.size() > sizeof(ApplicationArea)) { - data.resize(sizeof(ApplicationArea)); - } - - memcpy(data.data(), tag_data.application_area.data(), data.size()); - - return ResultSuccess; -} - -Result NfpDevice::SetApplicationArea(std::span data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (!is_app_area_open) { - LOG_ERROR(Service_NFP, "Application area is not open"); - return WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized.Value() == 0) { - LOG_ERROR(Service_NFP, "Application area is not initialized"); - return ApplicationAreaIsNotInitialized; - } - - if (data.size() > sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return ResultUnknown; - } - - Common::TinyMT rng{}; - std::memcpy(tag_data.application_area.data(), data.data(), data.size()); - // Fill remaining data with random numbers - rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), - sizeof(ApplicationArea) - data.size()); - - if (tag_data.application_write_counter != counter_limit) { - tag_data.application_write_counter++; - } - - is_data_moddified = true; - - return ResultSuccess; -} - -Result NfpDevice::CreateApplicationArea(u32 access_id, std::span data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized.Value() != 0) { - LOG_ERROR(Service_NFP, "Application area already exist"); - return ApplicationAreaExist; - } - - return RecreateApplicationArea(access_id, data); -} - -Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (is_app_area_open) { - LOG_ERROR(Service_NFP, "Application area is open"); - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (data.size() > sizeof(ApplicationArea)) { - LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); - return WrongApplicationAreaSize; - } - - Common::TinyMT rng{}; - std::memcpy(tag_data.application_area.data(), data.data(), data.size()); - // Fill remaining data with random numbers - rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), - sizeof(ApplicationArea) - data.size()); - - if (tag_data.application_write_counter != counter_limit) { - tag_data.application_write_counter++; - } - - const u64 application_id = system.GetApplicationProcessProgramID(); - - tag_data.application_id_byte = - static_cast(application_id >> application_id_version_offset & 0xf); - tag_data.application_id = - RemoveVersionByte(application_id) | - (static_cast(AppAreaVersion::NintendoSwitch) << application_id_version_offset); - tag_data.settings.settings.appdata_initialized.Assign(1); - tag_data.application_area_id = access_id; - tag_data.unknown = {}; - tag_data.unknown2 = {}; - - UpdateRegisterInfoCrc(); - - return Flush(); -} - -Result NfpDevice::DeleteApplicationArea() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - if (tag_data.settings.settings.appdata_initialized == 0) { - return ApplicationAreaIsNotInitialized; - } - - if (tag_data.application_write_counter != counter_limit) { - tag_data.application_write_counter++; - } - - Common::TinyMT rng{}; - rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); - rng.GenerateRandomBytes(&tag_data.application_id, sizeof(u64)); - rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); - rng.GenerateRandomBytes(&tag_data.application_id_byte, sizeof(u8)); - tag_data.settings.settings.appdata_initialized.Assign(0); - tag_data.unknown = {}; - tag_data.unknown2 = {}; - is_app_area_open = false; - - UpdateRegisterInfoCrc(); - - return Flush(); -} - -Result NfpDevice::ExistApplicationArea(bool& has_application_area) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0; - - return ResultSuccess; -} - -Result NfpDevice::GetAll(NfpData& data) const { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - CommonInfo common_info{}; - Service::Mii::MiiManager manager; - const u64 application_id = tag_data.application_id; - - GetCommonInfo(common_info); - - data = { - .magic = tag_data.constant_value, - .write_counter = tag_data.write_counter, - .settings_crc = tag_data.settings.crc, - .common_info = common_info, - .mii_char_info = tag_data.owner_mii, - .mii_store_data_extension = tag_data.mii_extension, - .creation_date = tag_data.settings.init_date.GetWriteDate(), - .amiibo_name = tag_data.settings.amiibo_name, - .amiibo_name_null_terminated = 0, - .settings = tag_data.settings.settings, - .unknown1 = tag_data.unknown, - .register_info_crc = tag_data.register_info_crc, - .unknown2 = tag_data.unknown2, - .application_id = application_id, - .access_id = tag_data.application_area_id, - .settings_crc_counter = tag_data.settings.crc_counter, - .font_region = tag_data.settings.settings.font_region, - .tag_type = PackedTagType::Type2, - .console_type = - static_cast(application_id >> application_id_version_offset & 0xf), - .application_id_byte = tag_data.application_id_byte, - .application_area = tag_data.application_area, - }; - - return ResultSuccess; -} - -Result NfpDevice::SetAll(const NfpData& data) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - tag_data.constant_value = data.magic; - tag_data.write_counter = data.write_counter; - tag_data.settings.crc = data.settings_crc; - tag_data.settings.write_date.SetWriteDate(data.common_info.last_write_date); - tag_data.write_counter = data.common_info.write_counter; - tag_data.amiibo_version = data.common_info.version; - tag_data.owner_mii = data.mii_char_info; - tag_data.mii_extension = data.mii_store_data_extension; - tag_data.settings.init_date.SetWriteDate(data.creation_date); - tag_data.settings.amiibo_name = data.amiibo_name; - tag_data.settings.settings = data.settings; - tag_data.unknown = data.unknown1; - tag_data.register_info_crc = data.register_info_crc; - tag_data.unknown2 = data.unknown2; - tag_data.application_id = data.application_id; - tag_data.application_area_id = data.access_id; - tag_data.settings.crc_counter = data.settings_crc_counter; - tag_data.settings.settings.font_region.Assign(data.font_region); - tag_data.application_id_byte = data.application_id_byte; - tag_data.application_area = data.application_area; - - return ResultSuccess; -} - -Result NfpDevice::BreakTag(BreakType break_type) { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - // TODO: Complete this implementation - - return FlushWithBreak(break_type); -} - -Result NfpDevice::ReadBackupData() { - // Not implemented - return ResultSuccess; -} - -Result NfpDevice::WriteBackupData() { - // Not implemented - return ResultSuccess; -} - -Result NfpDevice::WriteNtf() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; - } - - if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { - LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); - return WrongDeviceState; - } - - // Not implemented - - return ResultSuccess; -} - -u64 NfpDevice::GetHandle() const { - // Generate a handle based of the npad id - return static_cast(npad_id); -} - -u32 NfpDevice::GetApplicationAreaSize() const { - return sizeof(ApplicationArea); -} - -DeviceState NfpDevice::GetCurrentState() const { - return device_state; -} - -Core::HID::NpadIdType NfpDevice::GetNpadId() const { - return npad_id; -} - -AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const { - std::array settings_amiibo_name{}; - AmiiboName amiibo_name{}; - - // Convert from big endian to little endian - for (std::size_t i = 0; i < amiibo_name_length; i++) { - settings_amiibo_name[i] = static_cast(settings.amiibo_name[i]); - } - - // Convert from utf16 to utf8 - const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); - memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); - - return amiibo_name; -} - -void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { - std::array settings_amiibo_name{}; - - // Convert from utf8 to utf16 - const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); - memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), - amiibo_name_utf16.size() * sizeof(char16_t)); - - // Convert from little endian to big endian - for (std::size_t i = 0; i < amiibo_name_length; i++) { - settings.amiibo_name[i] = static_cast(settings_amiibo_name[i]); - } -} - -AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { - const auto& time_zone_manager = - system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); - Time::TimeZone::CalendarInfo calendar_info{}; - AmiiboDate amiibo_date{}; - - amiibo_date.SetYear(2000); - amiibo_date.SetMonth(1); - amiibo_date.SetDay(1); - - if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { - amiibo_date.SetYear(calendar_info.time.year); - amiibo_date.SetMonth(calendar_info.time.month); - amiibo_date.SetDay(calendar_info.time.day); - } - - return amiibo_date; -} - -u64 NfpDevice::RemoveVersionByte(u64 application_id) const { - return application_id & ~(0xfULL << application_id_version_offset); -} - -void NfpDevice::UpdateSettingsCrc() { - auto& settings = tag_data.settings; - - if (settings.crc_counter != counter_limit) { - settings.crc_counter++; - } - - // TODO: this reads data from a global, find what it is - std::array unknown_input{}; - boost::crc_32_type crc; - crc.process_bytes(&unknown_input, sizeof(unknown_input)); - settings.crc = crc.checksum(); -} - -void NfpDevice::UpdateRegisterInfoCrc() { -#pragma pack(push, 1) - struct CrcData { - Mii::Ver3StoreData mii; - u8 application_id_byte; - u8 unknown; - Mii::NfpStoreDataExtension mii_extension; - std::array unknown2; - }; - static_assert(sizeof(CrcData) == 0x7e, "CrcData is an invalid size"); -#pragma pack(pop) - - const CrcData crc_data{ - .mii = tag_data.owner_mii, - .application_id_byte = tag_data.application_id_byte, - .unknown = tag_data.unknown, - .mii_extension = tag_data.mii_extension, - .unknown2 = tag_data.unknown2, - }; - - boost::crc_32_type crc; - crc.process_bytes(&crc_data, sizeof(CrcData)); - tag_data.register_info_crc = crc.checksum(); -} - -} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h deleted file mode 100644 index bab05538a..000000000 --- a/src/core/hle/service/nfp/nfp_device.h +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "common/common_types.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nfp/nfp_types.h" -#include "core/hle/service/service.h" - -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Core { -class System; -} // namespace Core - -namespace Core::HID { -class EmulatedController; -enum class ControllerTriggerType; -enum class NpadIdType : u32; -} // namespace Core::HID - -namespace Service::NFP { -class NfpDevice { -public: - NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, - KernelHelpers::ServiceContext& service_context_, - Kernel::KEvent* availability_change_event_); - ~NfpDevice(); - - void Initialize(); - void Finalize(); - - Result StartDetection(TagProtocol allowed_protocol); - Result StopDetection(); - Result Mount(MountTarget mount_target); - Result Unmount(); - - Result Flush(); - Result FlushDebug(); - Result FlushWithBreak(BreakType break_type); - - Result GetTagInfo(TagInfo& tag_info) const; - Result GetCommonInfo(CommonInfo& common_info) const; - Result GetModelInfo(ModelInfo& model_info) const; - Result GetRegisterInfo(RegisterInfo& register_info) const; - Result GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const; - Result GetAdminInfo(AdminInfo& admin_info) const; - - Result DeleteRegisterInfo(); - Result SetRegisterInfoPrivate(const AmiiboName& amiibo_name); - Result RestoreAmiibo(); - Result Format(); - - Result OpenApplicationArea(u32 access_id); - Result GetApplicationAreaId(u32& application_area_id) const; - Result GetApplicationArea(std::vector& data) const; - Result SetApplicationArea(std::span data); - Result CreateApplicationArea(u32 access_id, std::span data); - Result RecreateApplicationArea(u32 access_id, std::span data); - Result DeleteApplicationArea(); - Result ExistApplicationArea(bool& has_application_area); - - Result GetAll(NfpData& data) const; - Result SetAll(const NfpData& data); - Result BreakTag(BreakType break_type); - Result ReadBackupData(); - Result WriteBackupData(); - Result WriteNtf(); - - u64 GetHandle() const; - u32 GetApplicationAreaSize() const; - DeviceState GetCurrentState() const; - Core::HID::NpadIdType GetNpadId() const; - - Kernel::KReadableEvent& GetActivateEvent() const; - Kernel::KReadableEvent& GetDeactivateEvent() const; - -private: - void NpadUpdate(Core::HID::ControllerTriggerType type); - bool LoadAmiibo(std::span data); - void CloseAmiibo(); - - AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; - void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); - AmiiboDate GetAmiiboDate(s64 posix_time) const; - u64 RemoveVersionByte(u64 application_id) const; - void UpdateSettingsCrc(); - void UpdateRegisterInfoCrc(); - - bool is_controller_set{}; - int callback_key; - const Core::HID::NpadIdType npad_id; - Core::System& system; - Core::HID::EmulatedController* npad_device = nullptr; - KernelHelpers::ServiceContext& service_context; - Kernel::KEvent* activate_event = nullptr; - Kernel::KEvent* deactivate_event = nullptr; - Kernel::KEvent* availability_change_event = nullptr; - - bool is_initalized{}; - bool is_data_moddified{}; - bool is_app_area_open{}; - bool is_plain_amiibo{}; - TagProtocol allowed_protocols{}; - s64 current_posix_time{}; - MountTarget mount_target{MountTarget::None}; - DeviceState device_state{DeviceState::Unavailable}; - - NTAG215File tag_data{}; - EncryptedNTAG215File encrypted_tag_data{}; -}; - -} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp index 2ed8bb1ba..21d159154 100644 --- a/src/core/hle/service/nfp/nfp_interface.cpp +++ b/src/core/hle/service/nfp/nfp_interface.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" @@ -6,198 +6,34 @@ #include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfc/common/device.h" +#include "core/hle/service/nfc/common/device_manager.h" +#include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_interface.h" #include "core/hle/service/nfp/nfp_result.h" +#include "core/hle/service/nfp/nfp_types.h" namespace Service::NFP { Interface::Interface(Core::System& system_, const char* name) - : ServiceFramework{system_, name}, service_context{system_, service_name} { - availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); + : NfcInterface{system_, name, NFC::BackendType::Nfp} {} - for (u32 device_index = 0; device_index < 10; device_index++) { - devices[device_index] = - std::make_shared(Core::HID::IndexToNpadIdType(device_index), system, - service_context, availability_change_event); - } -} - -Interface::~Interface() { - availability_change_event->Close(); -} - -void Interface::Initialize(HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::Initialized; - - for (auto& device : devices) { - device->Initialize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} +Interface::~Interface() = default; void Interface::InitializeSystem(HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::Initialized; - - for (auto& device : devices) { - device->Initialize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + Initialize(ctx); } void Interface::InitializeDebug(HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::Initialized; - - for (auto& device : devices) { - device->Initialize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Interface::Finalize(HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::NonInitialized; - - for (auto& device : devices) { - device->Finalize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + Initialize(ctx); } void Interface::FinalizeSystem(HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::NonInitialized; - - for (auto& device : devices) { - device->Finalize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + Finalize(ctx); } void Interface::FinalizeDebug(HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - state = State::NonInitialized; - - for (auto& device : devices) { - device->Finalize(); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Interface::ListDevices(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - if (!ctx.CanWriteBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - if (ctx.GetWriteBufferSize() == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - std::vector nfp_devices; - const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements(); - - for (const auto& device : devices) { - if (nfp_devices.size() >= max_allowed_devices) { - continue; - } - if (device->GetCurrentState() != DeviceState::Unavailable) { - nfp_devices.push_back(device->GetHandle()); - } - } - - if (nfp_devices.empty()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - ctx.WriteBuffer(nfp_devices); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(nfp_devices.size())); -} - -void Interface::StartDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - const auto nfp_protocol{rp.PopEnum()}; - LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->StartDetection(nfp_protocol); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Interface::StopDetection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->StopDetection(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + Finalize(ctx); } void Interface::Mount(HLERequestContext& ctx) { @@ -208,21 +44,9 @@ void Interface::Mount(HLERequestContext& ctx) { LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, model_type, mount_target); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + auto result = GetManager()->Mount(device_handle, model_type, mount_target); + result = TranslateResultToServiceError(result); - const auto result = device.value()->Mount(mount_target); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -232,21 +56,9 @@ void Interface::Unmount(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + auto result = GetManager()->Unmount(device_handle); + result = TranslateResultToServiceError(result); - const auto result = device.value()->Unmount(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -257,21 +69,9 @@ void Interface::OpenApplicationArea(HLERequestContext& ctx) { const auto access_id{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + auto result = GetManager()->OpenApplicationArea(device_handle, access_id); + result = TranslateResultToServiceError(result); - const auto result = device.value()->OpenApplicationArea(access_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -282,28 +82,16 @@ void Interface::GetApplicationArea(HLERequestContext& ctx) { const auto data_size = ctx.GetWriteBufferSize(); LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - if (!ctx.CanWriteBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - auto device = GetNfpDevice(device_handle); + std::vector data(data_size); + auto result = GetManager()->GetApplicationArea(device_handle, data); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { + if (result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); + rb.Push(result); return; } - std::vector data(data_size); - const auto result = device.value()->GetApplicationArea(data); ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(result); @@ -316,27 +104,9 @@ void Interface::SetApplicationArea(HLERequestContext& ctx) { const auto data{ctx.ReadBuffer()}; LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - if (!ctx.CanReadBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + auto result = GetManager()->SetApplicationArea(device_handle, data); + result = TranslateResultToServiceError(result); - const auto result = device.value()->SetApplicationArea(data); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -346,21 +116,9 @@ void Interface::Flush(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); + auto result = GetManager()->Flush(device_handle); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->Flush(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -370,21 +128,9 @@ void Interface::Restore(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + auto result = GetManager()->Restore(device_handle); + result = TranslateResultToServiceError(result); - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->RestoreAmiibo(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -397,53 +143,9 @@ void Interface::CreateApplicationArea(HLERequestContext& ctx) { LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, access_id, data.size()); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - if (!ctx.CanReadBuffer()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidArgument); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->CreateApplicationArea(access_id, data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Interface::GetTagInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + auto result = GetManager()->CreateApplicationArea(device_handle, access_id, data); + result = TranslateResultToServiceError(result); - TagInfo tag_info{}; - const auto result = device.value()->GetTagInfo(tag_info); - ctx.WriteBuffer(tag_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -453,23 +155,14 @@ void Interface::GetRegisterInfo(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); + RegisterInfo register_info{}; + auto result = GetManager()->GetRegisterInfo(device_handle, register_info); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(register_info); } - RegisterInfo register_info{}; - const auto result = device.value()->GetRegisterInfo(register_info); - ctx.WriteBuffer(register_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -479,23 +172,14 @@ void Interface::GetCommonInfo(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); + CommonInfo common_info{}; + auto result = GetManager()->GetCommonInfo(device_handle, common_info); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(common_info); } - CommonInfo common_info{}; - const auto result = device.value()->GetCommonInfo(common_info); - ctx.WriteBuffer(common_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -505,155 +189,26 @@ void Interface::GetModelInfo(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); + ModelInfo model_info{}; + auto result = GetManager()->GetModelInfo(device_handle, model_info); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(model_info); } - ModelInfo model_info{}; - const auto result = device.value()->GetModelInfo(model_info); - ctx.WriteBuffer(model_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } -void Interface::AttachActivateEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetActivateEvent()); -} - -void Interface::AttachDeactivateEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(device.value()->GetDeactivateEvent()); -} - -void Interface::GetState(HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(state); -} - -void Interface::GetDeviceState(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetCurrentState()); -} - -void Interface::GetNpadId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(device.value()->GetNpadId()); -} - void Interface::GetApplicationAreaSize(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(device.value()->GetApplicationAreaSize()); -} - -void Interface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) { - LOG_INFO(Service_NFP, "called"); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); + rb.Push(GetManager()->GetApplicationAreaSize()); } void Interface::RecreateApplicationArea(HLERequestContext& ctx) { @@ -664,21 +219,9 @@ void Interface::RecreateApplicationArea(HLERequestContext& ctx) { LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, access_id, data.size()); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + auto result = GetManager()->RecreateApplicationArea(device_handle, access_id, data); + result = TranslateResultToServiceError(result); - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->RecreateApplicationArea(access_id, data); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -686,23 +229,11 @@ void Interface::RecreateApplicationArea(HLERequestContext& ctx) { void Interface::Format(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + auto result = GetManager()->Format(device_handle); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->Format(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -712,23 +243,14 @@ void Interface::GetAdminInfo(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); + AdminInfo admin_info{}; + auto result = GetManager()->GetAdminInfo(device_handle, admin_info); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(admin_info); } - AdminInfo admin_info{}; - const auto result = device.value()->GetAdminInfo(admin_info); - ctx.WriteBuffer(admin_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -738,23 +260,14 @@ void Interface::GetRegisterInfoPrivate(HLERequestContext& ctx) { const auto device_handle{rp.Pop()}; LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); + RegisterInfoPrivate register_info{}; + auto result = GetManager()->GetRegisterInfoPrivate(device_handle, register_info); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(register_info); } - RegisterInfoPrivate register_info{}; - const auto result = device.value()->GetRegisterInfoPrivate(register_info); - ctx.WriteBuffer(register_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -762,25 +275,15 @@ void Interface::GetRegisterInfoPrivate(HLERequestContext& ctx) { void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - const auto buffer{ctx.ReadBuffer()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}, buffer_size={}", device_handle, - buffer.size()); + const auto register_info_buffer{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, buffer_size={}", device_handle, + register_info_buffer.size()); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + RegisterInfoPrivate register_info{}; + memcpy(®ister_info, register_info_buffer.data(), sizeof(RegisterInfoPrivate)); + auto result = GetManager()->SetRegisterInfoPrivate(device_handle, register_info); + result = TranslateResultToServiceError(result); - const auto result = device.value()->SetRegisterInfoPrivate({}); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -788,23 +291,11 @@ void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) { void Interface::DeleteRegisterInfo(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + auto result = GetManager()->DeleteRegisterInfo(device_handle); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->DeleteRegisterInfo(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -812,23 +303,11 @@ void Interface::DeleteRegisterInfo(HLERequestContext& ctx) { void Interface::DeleteApplicationArea(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + auto result = GetManager()->DeleteApplicationArea(device_handle); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->DeleteApplicationArea(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -836,24 +315,18 @@ void Interface::DeleteApplicationArea(HLERequestContext& ctx) { void Interface::ExistsApplicationArea(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + bool has_application_area = false; + auto result = GetManager()->ExistsApplicationArea(device_handle, has_application_area); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { + if (result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); + rb.Push(result); return; } - bool has_application_area = false; - const auto result = device.value()->ExistApplicationArea(has_application_area); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(result); rb.Push(has_application_area); @@ -862,27 +335,16 @@ void Interface::ExistsApplicationArea(HLERequestContext& ctx) { void Interface::GetAll(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + NfpData nfp_data{}; + auto result = GetManager()->GetAll(device_handle, nfp_data); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(nfp_data); } - NfpData data{}; - const auto result = device.value()->GetAll(data); - - ctx.WriteBuffer(data); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -890,28 +352,15 @@ void Interface::GetAll(HLERequestContext& ctx) { void Interface::SetAll(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - const auto nfp_data{ctx.ReadBuffer()}; - - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + const auto nfp_data_buffer{ctx.ReadBuffer()}; - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - NfpData data{}; - memcpy(&data, nfp_data.data(), sizeof(NfpData)); + NfpData nfp_data{}; + memcpy(&nfp_data, nfp_data_buffer.data(), sizeof(NfpData)); + auto result = GetManager()->SetAll(device_handle, nfp_data); + result = TranslateResultToServiceError(result); - const auto result = device.value()->SetAll(data); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -919,23 +368,11 @@ void Interface::SetAll(HLERequestContext& ctx) { void Interface::FlushDebug(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + auto result = GetManager()->FlushDebug(device_handle); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->FlushDebug(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -944,23 +381,12 @@ void Interface::BreakTag(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; const auto break_type{rp.PopEnum()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}, break_type={}", device_handle, break_type); + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, break_type={}", device_handle, + break_type); - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + auto result = GetManager()->BreakTag(device_handle, break_type); + result = TranslateResultToServiceError(result); - auto device = GetNfpDevice(device_handle); - - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->BreakTag(break_type); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -968,23 +394,16 @@ void Interface::BreakTag(HLERequestContext& ctx) { void Interface::ReadBackupData(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + std::vector backup_data{}; + auto result = GetManager()->ReadBackupData(device_handle, backup_data); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; + if (result.IsSuccess()) { + ctx.WriteBuffer(backup_data); } - const auto result = device.value()->ReadBackupData(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -992,23 +411,12 @@ void Interface::ReadBackupData(HLERequestContext& ctx) { void Interface::WriteBackupData(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } - - auto device = GetNfpDevice(device_handle); + const auto backup_data_buffer{ctx.ReadBuffer()}; + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } + auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer); + result = TranslateResultToServiceError(result); - const auto result = device.value()->WriteBackupData(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } @@ -1016,34 +424,15 @@ void Interface::WriteBackupData(HLERequestContext& ctx) { void Interface::WriteNtf(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); - - if (state == State::NonInitialized) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(NfcDisabled); - return; - } + const auto write_type{rp.PopEnum()}; + const auto ntf_data_buffer{ctx.ReadBuffer()}; + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); - auto device = GetNfpDevice(device_handle); + auto result = GetManager()->WriteNtf(device_handle, write_type, ntf_data_buffer); + result = TranslateResultToServiceError(result); - if (!device.has_value()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(DeviceNotFound); - return; - } - - const auto result = device.value()->WriteNtf(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } -std::optional> Interface::GetNfpDevice(u64 handle) { - for (auto& device : devices) { - if (device->GetHandle() == handle) { - return device; - } - } - return std::nullopt; -} - } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_interface.h b/src/core/hle/service/nfp/nfp_interface.h index 616c94b06..fa985b068 100644 --- a/src/core/hle/service/nfp/nfp_interface.h +++ b/src/core/hle/service/nfp/nfp_interface.h @@ -1,32 +1,23 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include -#include -#include - #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfc/nfc_interface.h" #include "core/hle/service/service.h" namespace Service::NFP { -class NfpDevice; -class Interface : public ServiceFramework { +class Interface : public NFC::NfcInterface { public: explicit Interface(Core::System& system_, const char* name); ~Interface() override; - void Initialize(HLERequestContext& ctx); void InitializeSystem(HLERequestContext& ctx); void InitializeDebug(HLERequestContext& ctx); - void Finalize(HLERequestContext& ctx); void FinalizeSystem(HLERequestContext& ctx); void FinalizeDebug(HLERequestContext& ctx); - void ListDevices(HLERequestContext& ctx); - void StartDetection(HLERequestContext& ctx); - void StopDetection(HLERequestContext& ctx); void Mount(HLERequestContext& ctx); void Unmount(HLERequestContext& ctx); void OpenApplicationArea(HLERequestContext& ctx); @@ -35,17 +26,10 @@ public: void Flush(HLERequestContext& ctx); void Restore(HLERequestContext& ctx); void CreateApplicationArea(HLERequestContext& ctx); - void GetTagInfo(HLERequestContext& ctx); void GetRegisterInfo(HLERequestContext& ctx); void GetCommonInfo(HLERequestContext& ctx); void GetModelInfo(HLERequestContext& ctx); - void AttachActivateEvent(HLERequestContext& ctx); - void AttachDeactivateEvent(HLERequestContext& ctx); - void GetState(HLERequestContext& ctx); - void GetDeviceState(HLERequestContext& ctx); - void GetNpadId(HLERequestContext& ctx); void GetApplicationAreaSize(HLERequestContext& ctx); - void AttachAvailabilityChangeEvent(HLERequestContext& ctx); void RecreateApplicationArea(HLERequestContext& ctx); void Format(HLERequestContext& ctx); void GetAdminInfo(HLERequestContext& ctx); @@ -61,21 +45,6 @@ public: void ReadBackupData(HLERequestContext& ctx); void WriteBackupData(HLERequestContext& ctx); void WriteNtf(HLERequestContext& ctx); - -private: - enum class State : u32 { - NonInitialized, - Initialized, - }; - - std::optional> GetNfpDevice(u64 handle); - - KernelHelpers::ServiceContext service_context; - - std::array, 10> devices{}; - - State state{State::NonInitialized}; - Kernel::KEvent* availability_change_event; }; } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h index d8e4cf094..4c126cd81 100644 --- a/src/core/hle/service/nfp/nfp_result.h +++ b/src/core/hle/service/nfp/nfp_result.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -7,18 +7,19 @@ namespace Service::NFP { -constexpr Result DeviceNotFound(ErrorModule::NFP, 64); -constexpr Result InvalidArgument(ErrorModule::NFP, 65); -constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68); -constexpr Result WrongDeviceState(ErrorModule::NFP, 73); -constexpr Result NfcDisabled(ErrorModule::NFP, 80); -constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); -constexpr Result TagRemoved(ErrorModule::NFP, 97); -constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); -constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); -constexpr Result CorruptedData(ErrorModule::NFP, 144); -constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); -constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); -constexpr Result NotAnAmiibo(ErrorModule::NFP, 178); +constexpr Result ResultDeviceNotFound(ErrorModule::NFP, 64); +constexpr Result ResultInvalidArgument(ErrorModule::NFP, 65); +constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68); +constexpr Result ResultWrongDeviceState(ErrorModule::NFP, 73); +constexpr Result ResultUnknown74(ErrorModule::NFC, 74); +constexpr Result ResultNfcDisabled(ErrorModule::NFP, 80); +constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result ResultTagRemoved(ErrorModule::NFP, 97); +constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); +constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); +constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); +constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); +constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 1ef047cee..7d36d5ee6 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -7,32 +7,19 @@ #include "common/swap.h" #include "core/hle/service/mii/types.h" +#include "core/hle/service/nfc/nfc_types.h" namespace Service::NFP { static constexpr std::size_t amiibo_name_length = 0xA; static constexpr std::size_t application_id_version_offset = 0x1c; static constexpr std::size_t counter_limit = 0xffff; -enum class ServiceType : u32 { - User, - Debug, - System, -}; - -enum class DeviceState : u32 { - Initialized, - SearchingForTag, - TagFound, - TagRemoved, - TagMounted, - Unavailable, - Finalized, -}; - +// This is nn::nfp::ModelType enum class ModelType : u32 { Amiibo, }; +// This is nn::nfp::MountTarget enum class MountTarget : u32 { None, Rom, @@ -72,35 +59,6 @@ enum class AmiiboSeries : u8 { Diablo, }; -enum class TagType : u32 { - None, - Type1, // ISO14443A RW 96-2k bytes 106kbit/s - Type2, // ISO14443A RW/RO 540 bytes 106kbit/s - Type3, // Sony Felica RW/RO 2k bytes 212kbit/s - Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s - Type5, // ISO15693 RW/RO 540 bytes 106kbit/s -}; - -enum class PackedTagType : u8 { - None, - Type1, // ISO14443A RW 96-2k bytes 106kbit/s - Type2, // ISO14443A RW/RO 540 bytes 106kbit/s - Type3, // Sony Felica RW/RO 2k bytes 212kbit/s - Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s - Type5, // ISO15693 RW/RO 540 bytes 106kbit/s -}; - -// Verify this enum. It might be completely wrong default protocol is 0x48 -enum class TagProtocol : u32 { - None, - TypeA = 1U << 0, // ISO14443A - TypeB = 1U << 1, // ISO14443B - TypeF = 1U << 2, // Sony Felica - Unknown1 = 1U << 3, - Unknown2 = 1U << 5, - All = 0xFFFFFFFFU, -}; - enum class AppAreaVersion : u8 { Nintendo3DS = 0, NintendoWiiU = 1, @@ -115,6 +73,11 @@ enum class BreakType : u32 { Unknown2, }; +enum class WriteType : u32 { + Unknown0, + Unknown1, +}; + enum class CabinetMode : u8 { StartNicknameAndOwnerSettings, StartGameDataEraser, @@ -122,27 +85,16 @@ enum class CabinetMode : u8 { StartFormatter, }; -enum class MifareCmd : u8 { - AuthA = 0x60, - AuthB = 0x61, - Read = 0x30, - Write = 0xA0, - Transfer = 0xB0, - Decrement = 0xC0, - Increment = 0xC1, - Store = 0xC2 -}; - -using UniqueSerialNumber = std::array; using LockBytes = std::array; using HashData = std::array; using ApplicationArea = std::array; using AmiiboName = std::array; -using DataBlock = std::array; -using KeyData = std::array; + +// This is nn::nfp::TagInfo +using TagInfo = NFC::TagInfo; struct TagUuid { - UniqueSerialNumber uid; + NFC::UniqueSerialNumber uid; u8 nintendo_id; LockBytes lock_bytes; }; @@ -243,7 +195,7 @@ struct AmiiboModelInfo { AmiiboType amiibo_type; u16_be model_number; AmiiboSeries series; - PackedTagType tag_type; + NFC::PackedTagType tag_type; INSERT_PADDING_BYTES(0x4); // Unknown }; static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); @@ -298,7 +250,7 @@ struct NTAG215File { u32_be register_info_crc; ApplicationArea application_area; // Encrypted Game data HashData hmac_tag; // Hash - UniqueSerialNumber uid; // Unique serial number + NFC::UniqueSerialNumber uid; // Unique serial number u8 nintendo_id; // Tag UUID AmiiboModelInfo model_info; HashData keygen_salt; // Salt @@ -326,17 +278,7 @@ static_assert(sizeof(EncryptedNTAG215File) == sizeof(NTAG215File), static_assert(std::is_trivially_copyable_v, "EncryptedNTAG215File must be trivially copyable."); -struct TagInfo { - UniqueSerialNumber uuid; - INSERT_PADDING_BYTES(0x3); - u8 uuid_length; - INSERT_PADDING_BYTES(0x15); - TagProtocol protocol; - TagType tag_type; - INSERT_PADDING_BYTES(0x30); -}; -static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); - +// This is nn::nfp::CommonInfo struct CommonInfo { WriteDate last_write_date; u16 write_counter; @@ -347,6 +289,7 @@ struct CommonInfo { }; static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); +// This is nn::nfp::ModelInfo struct ModelInfo { u16 character_id; u8 character_variant; @@ -357,6 +300,7 @@ struct ModelInfo { }; static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); +// This is nn::nfp::RegisterInfo struct RegisterInfo { Service::Mii::CharInfo mii_char_info; WriteDate creation_date; @@ -366,6 +310,7 @@ struct RegisterInfo { }; static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); +// This is nn::nfp::RegisterInfoPrivate struct RegisterInfoPrivate { Service::Mii::MiiStoreData mii_store_data; WriteDate creation_date; @@ -375,12 +320,13 @@ struct RegisterInfoPrivate { }; static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size"); +// This is nn::nfp::AdminInfo struct AdminInfo { u64 application_id; u32 application_area_id; u16 crc_change_counter; u8 flags; - PackedTagType tag_type; + NFC::PackedTagType tag_type; AppAreaVersion app_area_version; INSERT_PADDING_BYTES(0x7); INSERT_PADDING_BYTES(0x28); @@ -411,7 +357,7 @@ struct NfpData { u32 access_id; u16 settings_crc_counter; u8 font_region; - PackedTagType tag_type; + NFC::PackedTagType tag_type; AppAreaVersion console_type; u8 application_id_byte; INSERT_PADDING_BYTES(0x2E); @@ -420,37 +366,4 @@ struct NfpData { static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size"); #pragma pack() -struct SectorKey { - MifareCmd command; - u8 unknown; // Usually 1 - INSERT_PADDING_BYTES(0x6); - KeyData sector_key; - INSERT_PADDING_BYTES(0x2); -}; -static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size"); - -struct MifareReadBlockParameter { - u8 sector_number; - INSERT_PADDING_BYTES(0x7); - SectorKey sector_key; -}; -static_assert(sizeof(MifareReadBlockParameter) == 0x18, - "MifareReadBlockParameter is an invalid size"); - -struct MifareReadBlockData { - DataBlock data; - u8 sector_number; - INSERT_PADDING_BYTES(0x7); -}; -static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); - -struct MifareWriteBlockParameter { - DataBlock data; - u8 sector_number; - INSERT_PADDING_BYTES(0x7); - SectorKey sector_key; -}; -static_assert(sizeof(MifareWriteBlockParameter) == 0x28, - "MifareWriteBlockParameter is an invalid size"); - } // namespace Service::NFP diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp index 4559df5b1..4988fcc83 100644 --- a/src/yuzu/applets/qt_amiibo_settings.cpp +++ b/src/yuzu/applets/qt_amiibo_settings.cpp @@ -8,7 +8,7 @@ #include "common/assert.h" #include "common/string_util.h" -#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfp/nfp_result.h" #include "input_common/drivers/virtual_amiibo.h" #include "input_common/main.h" @@ -22,7 +22,7 @@ QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_, InputCommon::InputSubsystem* input_subsystem_, - std::shared_ptr nfp_device_) + std::shared_ptr nfp_device_) : QDialog(parent), ui(std::make_unique()), input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)}, parameters(std::move(parameters_)) { @@ -52,11 +52,11 @@ void QtAmiiboSettingsDialog::LoadInfo() { return; } - if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && - nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { + if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound && + nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) { return; } - nfp_device->Mount(Service::NFP::MountTarget::All); + nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All); LoadAmiiboInfo(); LoadAmiiboData(); @@ -261,7 +261,7 @@ void QtAmiiboSettings::Close() const { void QtAmiiboSettings::ShowCabinetApplet( const Core::Frontend::CabinetCallback& callback_, const Core::Frontend::CabinetParameters& parameters, - std::shared_ptr nfp_device) const { + std::shared_ptr nfp_device) const { callback = std::move(callback_); emit MainWindowShowAmiiboSettings(parameters, nfp_device); } diff --git a/src/yuzu/applets/qt_amiibo_settings.h b/src/yuzu/applets/qt_amiibo_settings.h index bc389a33f..ee66a0255 100644 --- a/src/yuzu/applets/qt_amiibo_settings.h +++ b/src/yuzu/applets/qt_amiibo_settings.h @@ -23,9 +23,9 @@ namespace Ui { class QtAmiiboSettingsDialog; } -namespace Service::NFP { -class NfpDevice; -} // namespace Service::NFP +namespace Service::NFC { +class NfcDevice; +} // namespace Service::NFC class QtAmiiboSettingsDialog final : public QDialog { Q_OBJECT @@ -33,7 +33,7 @@ class QtAmiiboSettingsDialog final : public QDialog { public: explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_, InputCommon::InputSubsystem* input_subsystem_, - std::shared_ptr nfp_device_); + std::shared_ptr nfp_device_); ~QtAmiiboSettingsDialog() override; int exec() override; @@ -52,7 +52,7 @@ private: std::unique_ptr ui; InputCommon::InputSubsystem* input_subsystem; - std::shared_ptr nfp_device; + std::shared_ptr nfp_device; // Parameters sent in from the backend HLE applet. Core::Frontend::CabinetParameters parameters; @@ -71,11 +71,11 @@ public: void Close() const override; void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_, const Core::Frontend::CabinetParameters& parameters, - std::shared_ptr nfp_device) const override; + std::shared_ptr nfp_device) const override; signals: void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters, - std::shared_ptr nfp_device) const; + std::shared_ptr nfp_device) const; void MainWindowRequestExit() const; private: diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b79409a68..e051e35df 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -570,8 +570,8 @@ void GMainWindow::RegisterMetaTypes() { // Cabinet Applet qRegisterMetaType("Core::Frontend::CabinetParameters"); - qRegisterMetaType>( - "std::shared_ptr"); + qRegisterMetaType>( + "std::shared_ptr"); // Controller Applet qRegisterMetaType("Core::Frontend::ControllerParameters"); @@ -599,7 +599,7 @@ void GMainWindow::RegisterMetaTypes() { } void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, - std::shared_ptr nfp_device) { + std::shared_ptr nfp_device) { cabinet_applet = new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device); SCOPE_EXIT({ diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8b5c1d747..ac90bd5ae 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -93,9 +93,9 @@ enum class SwkbdReplyType : u32; enum class WebExitReason : u32; } // namespace Service::AM::Applets -namespace Service::NFP { -class NfpDevice; -} // namespace Service::NFP +namespace Service::NFC { +class NfcDevice; +} // namespace Service::NFC namespace Ui { class MainWindow; @@ -188,7 +188,7 @@ public slots: void OnExit(); void OnSaveConfig(); void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, - std::shared_ptr nfp_device); + std::shared_ptr nfp_device); void AmiiboSettingsRequestExit(); void ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters); -- cgit v1.2.3