summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/nfc/mifare_user.cpp400
-rw-r--r--src/core/hle/service/nfc/mifare_user.h52
-rw-r--r--src/core/hle/service/nfc/nfc.cpp27
-rw-r--r--src/core/hle/service/nfc/nfc_device.cpp84
-rw-r--r--src/core/hle/service/nfc/nfc_device.h13
-rw-r--r--src/core/hle/service/nfc/nfc_result.h8
-rw-r--r--src/core/hle/service/nfc/nfc_user.cpp4
-rw-r--r--src/core/hle/service/nfp/nfp_types.h46
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp38
-rw-r--r--src/input_common/drivers/virtual_amiibo.h7
11 files changed, 629 insertions, 52 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ad8b8ef95..c6b5ac196 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -528,6 +528,8 @@ 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/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfc/nfc_device.cpp
diff --git a/src/core/hle/service/nfc/mifare_user.cpp b/src/core/hle/service/nfc/mifare_user.cpp
new file mode 100644
index 000000000..51523a3ae
--- /dev/null
+++ b/src/core/hle/service/nfc/mifare_user.cpp
@@ -0,0 +1,400 @@
+// 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/ipc_helpers.h"
+#include "core/hle/kernel/k_event.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<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
+ service_context, availability_change_event);
+ }
+}
+
+MFIUser ::~MFIUser() {
+ availability_change_event->Close();
+}
+
+void MFIUser::Initialize(Kernel::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(Kernel::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(Kernel::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<u64> nfp_devices;
+ const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
+
+ 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<s32>(nfp_devices.size()));
+}
+
+void MFIUser::StartDetection(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ 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(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ 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(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto buffer{ctx.ReadBuffer()};
+ const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()};
+ std::vector<NFP::MifareReadBlockParameter> 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<NFP::MifareReadBlockData> 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(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto buffer{ctx.ReadBuffer()};
+ const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()};
+ std::vector<NFP::MifareWriteBlockParameter> 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<NFP::MifareReadBlockData> 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(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ 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(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ 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(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ 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(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(state);
+}
+
+void MFIUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ 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(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ 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(Kernel::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<std::shared_ptr<NfcDevice>> 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
new file mode 100644
index 000000000..0e0638cb6
--- /dev/null
+++ b/src/core/hle/service/nfc/mifare_user.h
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <optional>
+
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NFC {
+class NfcDevice;
+
+class MFIUser final : public ServiceFramework<MFIUser> {
+public:
+ explicit MFIUser(Core::System& system_);
+ ~MFIUser();
+
+private:
+ enum class State : u32 {
+ NonInitialized,
+ Initialized,
+ };
+
+ void Initialize(Kernel::HLERequestContext& ctx);
+ void Finalize(Kernel::HLERequestContext& ctx);
+ void ListDevices(Kernel::HLERequestContext& ctx);
+ void StartDetection(Kernel::HLERequestContext& ctx);
+ void StopDetection(Kernel::HLERequestContext& ctx);
+ void Read(Kernel::HLERequestContext& ctx);
+ void Write(Kernel::HLERequestContext& ctx);
+ void GetTagInfo(Kernel::HLERequestContext& ctx);
+ void GetActivateEventHandle(Kernel::HLERequestContext& ctx);
+ void GetDeactivateEventHandle(Kernel::HLERequestContext& ctx);
+ void GetState(Kernel::HLERequestContext& ctx);
+ void GetDeviceState(Kernel::HLERequestContext& ctx);
+ void GetNpadId(Kernel::HLERequestContext& ctx);
+ void GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx);
+
+ std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
+
+ KernelHelpers::ServiceContext service_context;
+
+ std::array<std::shared_ptr<NfcDevice>, 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 2f4bacb3b..b17b18ab9 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -6,6 +6,7 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/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/service.h"
@@ -50,32 +51,6 @@ private:
}
};
-class MFIUser final : public ServiceFramework<MFIUser> {
-public:
- explicit MFIUser(Core::System& system_) : ServiceFramework{system_, "NFC::MFIUser"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "Initialize"},
- {1, nullptr, "Finalize"},
- {2, nullptr, "ListDevices"},
- {3, nullptr, "StartDetection"},
- {4, nullptr, "StopDetection"},
- {5, nullptr, "Read"},
- {6, nullptr, "Write"},
- {7, nullptr, "GetTagInfo"},
- {8, nullptr, "GetActivateEventHandle"},
- {9, nullptr, "GetDeactivateEventHandle"},
- {10, nullptr, "GetState"},
- {11, nullptr, "GetDeviceState"},
- {12, nullptr, "GetNpadId"},
- {13, nullptr, "GetAvailabilityChangeEventHandle"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
class NFC_MF_U final : public ServiceFramework<NFC_MF_U> {
public:
explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp
index 4d514cf5f..78578f723 100644
--- a/src/core/hle/service/nfc/nfc_device.cpp
+++ b/src/core/hle/service/nfc/nfc_device.cpp
@@ -77,11 +77,13 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
return false;
}
- if (data.size() != sizeof(NFP::EncryptedNTAG215File)) {
+ 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;
@@ -121,7 +123,7 @@ void NfcDevice::Finalize() {
device_state = NFP::DeviceState::Unavailable;
}
-Result NfcDevice::StartDetection(s32 protocol_) {
+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);
@@ -134,7 +136,7 @@ Result NfcDevice::StartDetection(s32 protocol_) {
}
device_state = NFP::DeviceState::SearchingForTag;
- protocol = protocol_;
+ allowed_protocols = allowed_protocol;
return ResultSuccess;
}
@@ -160,7 +162,7 @@ Result NfcDevice::StopDetection() {
return WrongDeviceState;
}
-Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
+Result NfcDevice::Flush() {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
@@ -170,6 +172,34 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
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<u8>(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,
@@ -181,6 +211,52 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
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<u64>(npad_id);
diff --git a/src/core/hle/service/nfc/nfc_device.h b/src/core/hle/service/nfc/nfc_device.h
index fa1348f1a..a6e114d36 100644
--- a/src/core/hle/service/nfc/nfc_device.h
+++ b/src/core/hle/service/nfc/nfc_device.h
@@ -34,10 +34,16 @@ public:
void Initialize();
void Finalize();
- Result StartDetection(s32 protocol_);
+ Result StartDetection(NFP::TagProtocol allowed_protocol);
Result StopDetection();
+ Result Flush();
- Result GetTagInfo(NFP::TagInfo& tag_info) const;
+ 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;
@@ -61,10 +67,11 @@ private:
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
- s32 protocol{};
+ NFP::TagProtocol allowed_protocols{};
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
NFP::EncryptedNTAG215File encrypted_tag_data{};
+ std::vector<u8> tag_data{};
};
} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h
index 537dc15f4..146b8ba61 100644
--- a/src/core/hle/service/nfc/nfc_result.h
+++ b/src/core/hle/service/nfc/nfc_result.h
@@ -12,6 +12,12 @@ 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 CorruptedData(ErrorModule::NFC, 144);
+
+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);
} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp
index ced2d560b..4615697e2 100644
--- a/src/core/hle/service/nfc/nfc_user.cpp
+++ b/src/core/hle/service/nfc/nfc_user.cpp
@@ -201,7 +201,7 @@ void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
- const auto nfp_protocol{rp.Pop<s32>()};
+ const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
if (state == State::NonInitialized) {
@@ -267,7 +267,7 @@ void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
}
NFP::TagInfo tag_info{};
- const auto result = device.value()->GetTagInfo(tag_info);
+ const auto result = device.value()->GetTagInfo(tag_info, false);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 69858096a..fc228c2b2 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -106,11 +106,24 @@ 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<u8, 7>;
using LockBytes = std::array<u8, 2>;
using HashData = std::array<u8, 0x20>;
using ApplicationArea = std::array<u8, 0xD8>;
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
+using DataBlock = std::array<u8, 0x10>;
+using KeyData = std::array<u8, 0x6>;
struct TagUuid {
UniqueSerialNumber uid;
@@ -323,4 +336,37 @@ struct RegisterInfo {
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
+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/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 564a188e5..63ffaca67 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -47,20 +47,20 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(
Common::Input::NfcState VirtualAmiibo::WriteNfcData(
[[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
- const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
- Common::FS::FileType::BinaryFile};
+ const Common::FS::IOFile nfc_file{file_path, Common::FS::FileAccessMode::ReadWrite,
+ Common::FS::FileType::BinaryFile};
- if (!amiibo_file.IsOpen()) {
+ if (!nfc_file.IsOpen()) {
LOG_ERROR(Core, "Amiibo is already on use");
return Common::Input::NfcState::WriteFailed;
}
- if (!amiibo_file.Write(data)) {
+ if (!nfc_file.Write(data)) {
LOG_ERROR(Service_NFP, "Error writting to file");
return Common::Input::NfcState::WriteFailed;
}
- amiibo_data = data;
+ nfc_data = data;
return Common::Input::NfcState::Success;
}
@@ -70,32 +70,44 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
}
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
- const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
- Common::FS::FileType::BinaryFile};
+ const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read,
+ Common::FS::FileType::BinaryFile};
if (state != State::WaitingForAmiibo) {
return Info::WrongDeviceState;
}
- if (!amiibo_file.IsOpen()) {
+ if (!nfc_file.IsOpen()) {
return Info::UnableToLoad;
}
- amiibo_data.resize(amiibo_size);
-
- if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) {
+ switch (nfc_file.GetSize()) {
+ case AmiiboSize:
+ case AmiiboSizeWithoutPassword:
+ nfc_data.resize(AmiiboSize);
+ if (nfc_file.Read(nfc_data) < AmiiboSizeWithoutPassword) {
+ return Info::NotAnAmiibo;
+ }
+ break;
+ case MifareSize:
+ nfc_data.resize(MifareSize);
+ if (nfc_file.Read(nfc_data) < MifareSize) {
+ return Info::NotAnAmiibo;
+ }
+ break;
+ default:
return Info::NotAnAmiibo;
}
file_path = filename;
state = State::AmiiboIsOpen;
- SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
+ SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
return Info::Success;
}
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
if (state == State::AmiiboIsOpen) {
- SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
+ SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
return Info::Success;
}
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 9baeb3997..0f9dad333 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -53,12 +53,13 @@ public:
std::string GetLastFilePath() const;
private:
- static constexpr std::size_t amiibo_size = 0x21C;
- static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
+ static constexpr std::size_t AmiiboSize = 0x21C;
+ static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8;
+ static constexpr std::size_t MifareSize = 0x400;
std::string file_path{};
State state{State::Initialized};
- std::vector<u8> amiibo_data;
+ std::vector<u8> nfc_data;
Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
};
} // namespace InputCommon