Просмотр исходного кода

service: nfc: Implement mifare service

Narr the Reg 3 лет назад
Родитель
Сommit
a4725bcb73

+ 2 - 0
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

+ 400 - 0
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

+ 52 - 0
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

+ 1 - 26
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"} {

+ 80 - 4
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);

+ 10 - 3
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

+ 7 - 1
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

+ 2 - 2
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);

+ 46 - 0
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