diff options
25 files changed, 1165 insertions, 193 deletions
| diff --git a/src/common/input.h b/src/common/input.h index 66fb15f0a..ea30770ae 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -86,7 +86,7 @@ enum class NfcState {      NewAmiibo,      WaitingForAmiibo,      AmiiboRemoved, -    NotAnAmiibo, +    InvalidTagType,      NotSupported,      WrongDeviceState,      WriteFailed, @@ -218,8 +218,22 @@ struct CameraStatus {  };  struct NfcStatus { -    NfcState state{}; -    std::vector<u8> data{}; +    NfcState state{NfcState::Unknown}; +    u8 uuid_length; +    u8 protocol; +    u8 tag_type; +    std::array<u8, 10> uuid; +}; + +struct MifareData { +    u8 command; +    u8 sector; +    std::array<u8, 0x6> key; +    std::array<u8, 0x10> data; +}; + +struct MifareRequest { +    std::array<MifareData, 0x10> data;  };  // List of buttons to be passed to Qt that can be translated @@ -294,7 +308,7 @@ struct CallbackStatus {      BatteryStatus battery_status{};      VibrationStatus vibration_status{};      CameraFormat camera_status{CameraFormat::None}; -    NfcState nfc_status{NfcState::Unknown}; +    NfcStatus nfc_status{};      std::vector<u8> raw_data{};  }; @@ -356,9 +370,30 @@ public:          return NfcState::NotSupported;      } +    virtual NfcState StartNfcPolling() { +        return NfcState::NotSupported; +    } + +    virtual NfcState StopNfcPolling() { +        return NfcState::NotSupported; +    } + +    virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) { +        return NfcState::NotSupported; +    } +      virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {          return NfcState::NotSupported;      } + +    virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request, +                                    [[maybe_unused]] MifareRequest& out_data) { +        return NfcState::NotSupported; +    } + +    virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) { +        return NfcState::NotSupported; +    }  };  /// An abstract class template for a factory that can create input devices. diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 0a7777732..c937495f9 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -149,12 +149,16 @@ void EmulatedController::LoadDevices() {      camera_params[0] = right_joycon;      camera_params[0].Set("camera", true); -    camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; -    ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; -    nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};      nfc_params[1] = right_joycon;      nfc_params[1].Set("nfc", true); +    // Only map virtual devices to the first controller +    if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) { +        camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; +        ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; +        nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; +    } +      output_params[LeftIndex] = left_joycon;      output_params[RightIndex] = right_joycon;      output_params[2] = camera_params[1]; @@ -1176,10 +1180,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {          return;      } -    controller.nfc_state = { -        controller.nfc_values.state, -        controller.nfc_values.data, -    }; +    controller.nfc_state = controller.nfc_values;  }  bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { @@ -1308,6 +1309,73 @@ bool EmulatedController::HasNfc() const {      return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);  } +bool EmulatedController::AddNfcHandle() { +    nfc_handles++; +    return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) == +           Common::Input::DriverResult::Success; +} + +bool EmulatedController::RemoveNfcHandle() { +    nfc_handles--; +    if (nfc_handles <= 0) { +        return SetPollingMode(EmulatedDeviceIndex::RightIndex, +                              Common::Input::PollingMode::Active) == +               Common::Input::DriverResult::Success; +    } +    return true; +} + +bool EmulatedController::StartNfcPolling() { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    return nfc_output_device->StartNfcPolling() == Common::Input::NfcState::Success || +           nfc_virtual_output_device->StartNfcPolling() == Common::Input::NfcState::Success; +} + +bool EmulatedController::StopNfcPolling() { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    return nfc_output_device->StopNfcPolling() == Common::Input::NfcState::Success || +           nfc_virtual_output_device->StopNfcPolling() == Common::Input::NfcState::Success; +} + +bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) { +        return true; +    } + +    return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success; +} + +bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, +                                        Common::Input::MifareRequest& out_data) { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) { +        return true; +    } + +    return nfc_virtual_output_device->ReadMifareData(request, out_data) == +           Common::Input::NfcState::Success; +} + +bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) { +        return true; +    } + +    return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success; +} +  bool EmulatedController::WriteNfc(const std::vector<u8>& data) {      auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];      auto& nfc_virtual_output_device = output_devices[3]; diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 09fe1a0ab..d511e5fac 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -97,10 +97,7 @@ struct RingSensorForce {      f32 force;  }; -struct NfcState { -    Common::Input::NfcState state{}; -    std::vector<u8> data{}; -}; +using NfcState = Common::Input::NfcStatus;  struct ControllerMotion {      Common::Vec3f accel{}; @@ -393,9 +390,31 @@ public:      /// Returns true if the device has nfc support      bool HasNfc() const; +    /// Sets the joycon in nfc mode and increments the handle count +    bool AddNfcHandle(); + +    /// Decrements the handle count if zero sets the joycon in active mode +    bool RemoveNfcHandle(); + +    /// Start searching for nfc tags +    bool StartNfcPolling(); + +    /// Stop searching for nfc tags +    bool StopNfcPolling(); + +    /// Returns true if the nfc tag was readable +    bool ReadAmiiboData(std::vector<u8>& data); +      /// Returns true if the nfc tag was written      bool WriteNfc(const std::vector<u8>& data); +    /// Returns true if the nfc tag was readable +    bool ReadMifareData(const Common::Input::MifareRequest& request, +                        Common::Input::MifareRequest& out_data); + +    /// Returns true if the nfc tag was written +    bool WriteMifareData(const Common::Input::MifareRequest& request); +      /// Returns the led pattern corresponding to this emulated controller      LedPattern GetLedPattern() const; @@ -532,6 +551,7 @@ private:      bool system_buttons_enabled{true};      f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};      u32 turbo_button_state{0}; +    std::size_t nfc_handles{0};      // Temporary values to avoid doing changes while the controller is in configuring mode      NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 4ccb1c596..a05716fd8 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -299,11 +299,7 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal      Common::Input::NfcStatus nfc{};      switch (callback.type) {      case Common::Input::InputType::Nfc: -        nfc = { -            .state = callback.nfc_status, -            .data = callback.raw_data, -        }; -        break; +        return callback.nfc_status;      default:          LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);          break; diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 8b754e9d4..19ed184e8 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -141,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, false); +    const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);      nfp_device->Finalize();      if (reg_result.IsSuccess()) { diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index f4b180b06..5bf289818 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -93,7 +93,8 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {      const auto nfc_status = npad_device->GetNfc();      switch (nfc_status.state) {      case Common::Input::NfcState::NewAmiibo: -        LoadNfcTag(nfc_status.data); +        LoadNfcTag(nfc_status.protocol, nfc_status.tag_type, nfc_status.uuid_length, +                   nfc_status.uuid);          break;      case Common::Input::NfcState::AmiiboRemoved:          if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { @@ -108,28 +109,46 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {      }  } -bool NfcDevice::LoadNfcTag(std::span<const u8> data) { +bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid) {      if (device_state != DeviceState::SearchingForTag) {          LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);          return false;      } +    if ((protocol & static_cast<u8>(allowed_protocols)) == 0) { +        LOG_ERROR(Service_NFC, "Protocol not supported {}", protocol); +        return false; +    } + +    real_tag_info = { +        .uuid = uuid, +        .uuid_length = uuid_length, +        .protocol = static_cast<NfcProtocol>(protocol), +        .tag_type = static_cast<TagType>(tag_type), +    }; + +    device_state = DeviceState::TagFound; +    deactivate_event->GetReadableEvent().Clear(); +    activate_event->Signal(); +    return true; +} + +bool NfcDevice::LoadAmiiboData() { +    std::vector<u8> data{}; + +    if (!npad_device->ReadAmiiboData(data)) { +        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);      is_write_protected = false; -    device_state = DeviceState::TagFound; -    deactivate_event->GetReadableEvent().Clear(); -    activate_event->Signal(); -      // Fallback for plain amiibos      if (is_plain_amiibo) {          LOG_INFO(Service_NFP, "Using plain amiibo"); @@ -147,6 +166,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {          return true;      } +    LOG_INFO(Service_NFP, "Using encrypted amiibo");      tag_data = {};      memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));      return true; @@ -162,7 +182,6 @@ void NfcDevice::CloseNfcTag() {      device_state = DeviceState::TagRemoved;      encrypted_tag_data = {};      tag_data = {}; -    mifare_data = {};      activate_event->GetReadableEvent().Clear();      deactivate_event->Signal();  } @@ -179,8 +198,12 @@ void NfcDevice::Initialize() {      device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;      encrypted_tag_data = {};      tag_data = {}; -    mifare_data = {}; -    is_initalized = true; + +    if (device_state != DeviceState::Initialized) { +        return; +    } + +    is_initalized = npad_device->AddNfcHandle();  }  void NfcDevice::Finalize() { @@ -190,6 +213,11 @@ void NfcDevice::Finalize() {      if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {          StopDetection();      } + +    if (device_state != DeviceState::Unavailable) { +        npad_device->RemoveNfcHandle(); +    } +      device_state = DeviceState::Unavailable;      is_initalized = false;  } @@ -200,10 +228,8 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {          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"); +    if (!npad_device->StartNfcPolling()) { +        LOG_ERROR(Service_NFC, "Nfc polling not supported");          return ResultNfcDisabled;      } @@ -213,9 +239,6 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {  }  Result NfcDevice::StopDetection() { -    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, -                                Common::Input::PollingMode::Active); -      if (device_state == DeviceState::Initialized) {          return ResultSuccess;      } @@ -225,6 +248,7 @@ Result NfcDevice::StopDetection() {      }      if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { +        npad_device->StopNfcPolling();          device_state = DeviceState::Initialized;          return ResultSuccess;      } @@ -233,7 +257,7 @@ Result NfcDevice::StopDetection() {      return ResultWrongDeviceState;  } -Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { +Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {      if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {          LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);          if (device_state == DeviceState::TagRemoved) { @@ -242,41 +266,15 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {          return ResultWrongDeviceState;      } -    UniqueSerialNumber uuid{}; -    u8 uuid_length{}; -    NfcProtocol protocol{NfcProtocol::TypeA}; -    TagType tag_type{TagType::Type2}; - -    if (is_mifare) { -        tag_type = TagType::Mifare; -        uuid_length = sizeof(NFP::NtagTagUuid); -        memcpy(uuid.data(), mifare_data.data(), uuid_length); -    } else { -        tag_type = TagType::Type2; -        uuid_length = sizeof(NFP::NtagTagUuid); -        NFP::NtagTagUuid nUuid{ -            .part1 = encrypted_tag_data.uuid.part1, -            .part2 = encrypted_tag_data.uuid.part2, -            .nintendo_id = encrypted_tag_data.uuid.nintendo_id, -        }; -        memcpy(uuid.data(), &nUuid, uuid_length); +    tag_info = real_tag_info; -        // Generate random UUID to bypass amiibo load limits -        if (Settings::values.random_amiibo_id) { -            Common::TinyMT rng{}; -            rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); -            rng.GenerateRandomBytes(uuid.data(), uuid_length); -        } +    // Generate random UUID to bypass amiibo load limits +    if (real_tag_info.tag_type == TagType::Type2 && Settings::values.random_amiibo_id) { +        Common::TinyMT rng{}; +        rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); +        rng.GenerateRandomBytes(tag_info.uuid.data(), tag_info.uuid_length);      } -    // Protocol and tag type may change here -    tag_info = { -        .uuid = uuid, -        .uuid_length = uuid_length, -        .protocol = protocol, -        .tag_type = tag_type, -    }; -      return ResultSuccess;  } @@ -293,7 +291,7 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter      Result result = ResultSuccess;      TagInfo tag_info{}; -    result = GetTagInfo(tag_info, true); +    result = GetTagInfo(tag_info);      if (result.IsError()) {          return result; @@ -307,6 +305,8 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter          return ResultInvalidArgument;      } +    Common::Input::MifareRequest request{}; +    Common::Input::MifareRequest out_data{};      const auto unknown = parameters[0].sector_key.unknown;      for (std::size_t i = 0; i < parameters.size(); i++) {          if (unknown != parameters[i].sector_key.unknown) { @@ -315,25 +315,29 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter      }      for (std::size_t i = 0; i < parameters.size(); i++) { -        result = ReadMifare(parameters[i], read_block_data[i]); -        if (result.IsError()) { -            break; +        if (parameters[i].sector_key.command == MifareCmd::None) { +            continue;          } +        request.data[i].command = static_cast<u8>(parameters[i].sector_key.command); +        request.data[i].sector = parameters[i].sector_number; +        memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(), +               sizeof(KeyData));      } -    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 (mifare_data.size() < sector_index + sizeof(DataBlock)) { +    if (!npad_device->ReadMifareData(request, out_data)) {          return ResultMifareError288;      } -    // TODO: Use parameter.sector_key to read encrypted data -    memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock)); +    for (std::size_t i = 0; i < read_block_data.size(); i++) { +        if (static_cast<MifareCmd>(out_data.data[i].command) == MifareCmd::None) { +            continue; +        } + +        read_block_data[i] = { +            .data = out_data.data[i].data, +            .sector_number = out_data.data[i].sector, +        }; +    }      return ResultSuccess;  } @@ -342,7 +346,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet      Result result = ResultSuccess;      TagInfo tag_info{}; -    result = GetTagInfo(tag_info, true); +    result = GetTagInfo(tag_info);      if (result.IsError()) {          return result; @@ -363,42 +367,25 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet          }      } +    Common::Input::MifareRequest request{};      for (std::size_t i = 0; i < parameters.size(); i++) { -        result = WriteMifare(parameters[i]); -        if (result.IsError()) { -            break; +        if (parameters[i].sector_key.command == MifareCmd::None) { +            continue;          } +        request.data[i].command = static_cast<u8>(parameters[i].sector_key.command); +        request.data[i].sector = parameters[i].sector_number; +        memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(), +               sizeof(KeyData)); +        memcpy(request.data[i].data.data(), parameters[i].data.data(), sizeof(KeyData));      } -    if (!npad_device->WriteNfc(mifare_data)) { -        LOG_ERROR(Service_NFP, "Error writing to file"); +    if (!npad_device->WriteMifareData(request)) {          return ResultMifareError288;      }      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 ResultMifareError288; -    } - -    // 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<const u8> command_data,                                             std::span<u8> out_data) { @@ -412,6 +399,11 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target          return ResultWrongDeviceState;      } +    if (!LoadAmiiboData()) { +        LOG_ERROR(Service_NFP, "Not an amiibo"); +        return ResultInvalidTagType; +    } +      if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {          LOG_ERROR(Service_NFP, "Not an amiibo");          return ResultInvalidTagType; @@ -562,7 +554,7 @@ Result NfcDevice::Restore() {      NFC::TagInfo tag_info{};      std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; -    Result result = GetTagInfo(tag_info, false); +    Result result = GetTagInfo(tag_info);      if (result.IsError()) {          return result; @@ -635,7 +627,7 @@ Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const {      // TODO: Validate this data      common_info = {          .last_write_date = settings.write_date.GetWriteDate(), -        .write_counter = tag_data.write_counter, +        .write_counter = tag_data.application_write_counter,          .version = tag_data.amiibo_version,          .application_area_size = sizeof(NFP::ApplicationArea),      }; diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 7560210d6..0ed1ff34c 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -42,15 +42,12 @@ public:      Result StartDetection(NfcProtocol allowed_protocol);      Result StopDetection(); -    Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const; +    Result GetTagInfo(TagInfo& tag_info) const;      Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,                        std::span<MifareReadBlockData> read_block_data) const; -    Result ReadMifare(const MifareReadBlockParameter& parameter, -                      MifareReadBlockData& read_block_data) const;      Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); -    Result WriteMifare(const MifareWriteBlockParameter& parameter);      Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,                                      std::span<const u8> command_data, std::span<u8> out_data); @@ -105,7 +102,8 @@ public:  private:      void NpadUpdate(Core::HID::ControllerTriggerType type); -    bool LoadNfcTag(std::span<const u8> data); +    bool LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid); +    bool LoadAmiiboData();      void CloseNfcTag();      NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; @@ -140,8 +138,8 @@ private:      bool is_write_protected{};      NFP::MountTarget mount_target{NFP::MountTarget::None}; +    TagInfo real_tag_info{};      NFP::NTAG215File tag_data{}; -    std::vector<u8> mifare_data{};      NFP::EncryptedNTAG215File encrypted_tag_data{};  }; diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index b0456508e..562f3a28e 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -29,6 +29,9 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex  }  DeviceManager ::~DeviceManager() { +    if (is_initialized) { +        Finalize(); +    }      service_context.CloseEvent(availability_change_event);  } @@ -125,14 +128,14 @@ Result DeviceManager::StopDetection(u64 device_handle) {      return result;  } -Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const { +Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const {      std::scoped_lock lock{mutex};      std::shared_ptr<NfcDevice> device = nullptr;      auto result = GetDeviceHandle(device_handle, device);      if (result.IsSuccess()) { -        result = device->GetTagInfo(tag_info, is_mifare); +        result = device->GetTagInfo(tag_info);          result = VerifyDeviceResult(device, result);      } @@ -546,7 +549,7 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons      NFC::TagInfo tag_info{};      if (result.IsSuccess()) { -        result = device->GetTagInfo(tag_info, false); +        result = device->GetTagInfo(tag_info);      }      if (result.IsSuccess()) { @@ -565,7 +568,7 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat      NFC::TagInfo tag_info{};      if (result.IsSuccess()) { -        result = device->GetTagInfo(tag_info, false); +        result = device->GetTagInfo(tag_info);      }      if (result.IsSuccess()) { diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index 2971e280f..c61ba0cf3 100644 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -33,7 +33,7 @@ public:      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; +    Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const;      Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;      Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;      Result ReadMifare(u64 device_handle, diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h index 75b59f021..467937399 100644 --- a/src/core/hle/service/nfc/mifare_types.h +++ b/src/core/hle/service/nfc/mifare_types.h @@ -11,9 +11,10 @@  namespace Service::NFC {  enum class MifareCmd : u8 { +    None = 0x00, +    Read = 0x30,      AuthA = 0x60,      AuthB = 0x61, -    Read = 0x30,      Write = 0xA0,      Transfer = 0xB0,      Decrement = 0xC0, @@ -35,17 +36,17 @@ static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");  // This is nn::nfc::MifareReadBlockParameter  struct MifareReadBlockParameter { -    u8 sector_number; +    u8 sector_number{};      INSERT_PADDING_BYTES(0x7); -    SectorKey sector_key; +    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; +    DataBlock data{}; +    u8 sector_number{};      INSERT_PADDING_BYTES(0x7);  };  static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 130fb7f78..e7ca7582e 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -174,8 +174,7 @@ void NfcInterface::GetTagInfo(HLERequestContext& ctx) {      LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);      TagInfo tag_info{}; -    auto result = -        GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare); +    auto result = GetManager()->GetTagInfo(device_handle, tag_info);      result = TranslateResultToServiceError(result);      if (result.IsSuccess()) { @@ -216,8 +215,8 @@ void NfcInterface::ReadMifare(HLERequestContext& ctx) {      memcpy(read_commands.data(), buffer.data(),             number_of_commands * sizeof(MifareReadBlockParameter)); -    LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", -             device_handle, number_of_commands); +    LOG_INFO(Service_NFC, "called, device_handle={}, read_commands_size={}", device_handle, +             number_of_commands);      std::vector<MifareReadBlockData> out_data(number_of_commands);      auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data); diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index b2b5677c8..52494e0d9 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {                  OnMotionUpdate(port, type, id, value);              }},              .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, -            .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) { -                OnAmiiboUpdate(port, type, amiibo_data); +            .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) { +                OnAmiiboUpdate(port, type, tag_info);              }},              .on_camera_data = {[this, port](const std::vector<u8>& camera_data,                                              Joycon::IrsResolution format) { @@ -291,13 +291,105 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c      return Common::Input::NfcState::Success;  }; +Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } +    return TranslateDriverResult(handle->StartNfcPolling()); +}; + +Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } +    return TranslateDriverResult(handle->StopNfcPolling()); +}; + +Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier, +                                                std::vector<u8>& out_data) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } +    return TranslateDriverResult(handle->ReadAmiiboData(out_data)); +} +  Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,                                                const std::vector<u8>& data) {      auto handle = GetHandle(identifier); -    if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { -        return Common::Input::NfcState::WriteFailed; +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown;      } -    return Common::Input::NfcState::Success; +    return TranslateDriverResult(handle->WriteNfcData(data)); +}; + +Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier, +                                                const Common::Input::MifareRequest& request, +                                                Common::Input::MifareRequest& data) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } + +    const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); +    std::vector<Joycon::MifareReadChunk> read_request{}; +    for (const auto& request_data : request.data) { +        if (request_data.command == 0) { +            continue; +        } +        Joycon::MifareReadChunk chunk = { +            .command = command, +            .sector_key = {}, +            .sector = request_data.sector, +        }; +        memcpy(chunk.sector_key.data(), request_data.key.data(), +               sizeof(Joycon::MifareReadChunk::sector_key)); +        read_request.emplace_back(chunk); +    } + +    std::vector<Joycon::MifareReadData> read_data(read_request.size()); +    const auto result = handle->ReadMifareData(read_request, read_data); +    if (result == Joycon::DriverResult::Success) { +        for (std::size_t i = 0; i < read_request.size(); i++) { +            data.data[i] = { +                .command = static_cast<u8>(command), +                .sector = read_data[i].sector, +                .key = {}, +                .data = read_data[i].data, +            }; +        } +    } +    return TranslateDriverResult(result); +}; + +Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier, +                                                 const Common::Input::MifareRequest& request) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } + +    const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); +    std::vector<Joycon::MifareWriteChunk> write_request{}; +    for (const auto& request_data : request.data) { +        if (request_data.command == 0) { +            continue; +        } +        Joycon::MifareWriteChunk chunk = { +            .command = command, +            .sector_key = {}, +            .sector = request_data.sector, +            .data = {}, +        }; +        memcpy(chunk.sector_key.data(), request_data.key.data(), +               sizeof(Joycon::MifareReadChunk::sector_key)); +        memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data)); +        write_request.emplace_back(chunk); +    } + +    return TranslateDriverResult(handle->WriteMifareData(write_request));  };  Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, @@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) {  }  void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, -                             const std::vector<u8>& amiibo_data) { +                             const Joycon::TagInfo& tag_info) {      const auto identifier = GetIdentifier(port, type); -    const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved -                                               : Common::Input::NfcState::NewAmiibo; -    SetNfc(identifier, {nfc_state, amiibo_data}); +    const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved +                                                     : Common::Input::NfcState::NewAmiibo; + +    const Common::Input::NfcStatus nfc_status{ +        .state = nfc_state, +        .uuid_length = tag_info.uuid_length, +        .protocol = tag_info.protocol, +        .tag_type = tag_info.tag_type, +        .uuid = tag_info.uuid, +    }; + +    SetNfc(identifier, nfc_status);  }  void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, @@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {          return "Unknown Switch Controller";      }  } + +Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const { +    switch (result) { +    case Joycon::DriverResult::Success: +        return Common::Input::NfcState::Success; +    case Joycon::DriverResult::Disabled: +        return Common::Input::NfcState::WrongDeviceState; +    case Joycon::DriverResult::NotSupported: +        return Common::Input::NfcState::NotSupported; +    default: +        return Common::Input::NfcState::Unknown; +    } +} +  } // namespace InputCommon diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index e3f0ad78f..4c323d7d6 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -15,6 +15,7 @@ using SerialNumber = std::array<u8, 15>;  struct Battery;  struct Color;  struct MotionData; +struct TagInfo;  enum class ControllerType : u8;  enum class DriverResult;  enum class IrsResolution; @@ -39,9 +40,18 @@ public:      Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,                                                  Common::Input::CameraFormat camera_format) override; -    Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; -    Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, +    Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override; +    Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override; +    Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override; +    Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier, +                                           std::vector<u8>& out_data) override; +    Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,                                           const std::vector<u8>& data) override; +    Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier, +                                           const Common::Input::MifareRequest& request, +                                           Common::Input::MifareRequest& out_data) override; +    Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier, +                                            const Common::Input::MifareRequest& request) override;      Common::Input::DriverResult SetPollingMode(          const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; @@ -82,7 +92,7 @@ private:                          const Joycon::MotionData& value);      void OnRingConUpdate(f32 ring_data);      void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, -                        const std::vector<u8>& amiibo_data); +                        const Joycon::TagInfo& amiibo_data);      void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,                          Joycon::IrsResolution format); @@ -102,6 +112,8 @@ private:      /// Returns the name of the device in text format      std::string JoyconName(Joycon::ControllerType type) const; +    Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const; +      std::jthread scan_thread;      // Joycon types are split by type to ease supporting dualjoycon configurations diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 6435b8af8..180eb53ef 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -29,14 +29,13 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode(      switch (polling_mode) {      case Common::Input::PollingMode::NFC: -        if (state == State::Initialized) { -            state = State::WaitingForAmiibo; -        } +        state = State::Initialized;          return Common::Input::DriverResult::Success;      default: -        if (state == State::AmiiboIsOpen) { +        if (state == State::TagNearby) {              CloseAmiibo();          } +        state = State::Disabled;          return Common::Input::DriverResult::NotSupported;      }  } @@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(      [[maybe_unused]] const PadIdentifier& identifier_) const {      return Common::Input::NfcState::Success;  } +Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) { +    if (state != State::Initialized) { +        return Common::Input::NfcState::WrongDeviceState; +    } +    state = State::WaitingForAmiibo; +    return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) { +    if (state == State::Disabled) { +        return Common::Input::NfcState::WrongDeviceState; +    } +    if (state == State::TagNearby) { +        CloseAmiibo(); +    } +    state = State::Initialized; +    return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_, +                                                      std::vector<u8>& out_data) { +    if (state != State::TagNearby) { +        return Common::Input::NfcState::WrongDeviceState; +    } + +    if (status.tag_type != 1U << 1) { +        return Common::Input::NfcState::InvalidTagType; +    } + +    out_data.resize(nfc_data.size()); +    memcpy(out_data.data(), nfc_data.data(), nfc_data.size()); +    return Common::Input::NfcState::Success; +}  Common::Input::NfcState VirtualAmiibo::WriteNfcData(      [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { @@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(      return Common::Input::NfcState::Success;  } +Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_, +                                                      const Common::Input::MifareRequest& request, +                                                      Common::Input::MifareRequest& out_data) { +    if (state != State::TagNearby) { +        return Common::Input::NfcState::WrongDeviceState; +    } + +    if (status.tag_type != 1U << 6) { +        return Common::Input::NfcState::InvalidTagType; +    } + +    for (std::size_t i = 0; i < request.data.size(); i++) { +        if (request.data[i].command == 0) { +            continue; +        } +        out_data.data[i].command = request.data[i].command; +        out_data.data[i].sector = request.data[i].sector; + +        const std::size_t sector_index = +            request.data[i].sector * sizeof(Common::Input::MifareData::data); + +        if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { +            return Common::Input::NfcState::WriteFailed; +        } + +        // Ignore the sector key as we don't support it +        memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index, +               sizeof(Common::Input::MifareData::data)); +    } + +    return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::WriteMifareData( +    const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) { +    if (state != State::TagNearby) { +        return Common::Input::NfcState::WrongDeviceState; +    } + +    if (status.tag_type != 1U << 6) { +        return Common::Input::NfcState::InvalidTagType; +    } + +    for (std::size_t i = 0; i < request.data.size(); i++) { +        if (request.data[i].command == 0) { +            continue; +        } + +        const std::size_t sector_index = +            request.data[i].sector * sizeof(Common::Input::MifareData::data); + +        if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { +            return Common::Input::NfcState::WriteFailed; +        } + +        // Ignore the sector key as we don't support it +        memcpy(nfc_data.data() + sector_index, request.data[i].data.data(), +               sizeof(Common::Input::MifareData::data)); +    } + +    return Common::Input::NfcState::Success; +} +  VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {      return state;  } @@ -112,23 +207,31 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {      case AmiiboSizeWithoutPassword:      case AmiiboSizeWithSignature:          nfc_data.resize(AmiiboSize); +        status.tag_type = 1U << 1; +        status.uuid_length = 7;          break;      case MifareSize:          nfc_data.resize(MifareSize); +        status.tag_type = 1U << 6; +        status.uuid_length = 4;          break;      default:          return Info::NotAnAmiibo;      } -    state = State::AmiiboIsOpen; +    status.uuid = {}; +    status.protocol = 1; +    state = State::TagNearby; +    status.state = Common::Input::NfcState::NewAmiibo,      memcpy(nfc_data.data(), data.data(), data.size_bytes()); -    SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); +    memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length); +    SetNfc(identifier, status);      return Info::Success;  }  VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { -    if (state == State::AmiiboIsOpen) { -        SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); +    if (state == State::TagNearby) { +        SetNfc(identifier, status);          return Info::Success;      } @@ -136,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {  }  VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { -    state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo -                                                            : State::Initialized; -    SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); +    if (state != State::TagNearby) { +        return Info::Success; +    } + +    state = State::WaitingForAmiibo; +    status.state = Common::Input::NfcState::AmiiboRemoved; +    SetNfc(identifier, status); +    status.tag_type = 0;      return Info::Success;  } diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 09ca09e68..490f38e05 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -20,9 +20,10 @@ namespace InputCommon {  class VirtualAmiibo final : public InputEngine {  public:      enum class State { +        Disabled,          Initialized,          WaitingForAmiibo, -        AmiiboIsOpen, +        TagNearby,      };      enum class Info { @@ -41,9 +42,17 @@ public:          const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;      Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; - +    Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override; +    Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override; +    Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_, +                                           std::vector<u8>& out_data) override;      Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,                                           const std::vector<u8>& data) override; +    Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_, +                                           const Common::Input::MifareRequest& data, +                                           Common::Input::MifareRequest& out_data) override; +    Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_, +                                            const Common::Input::MifareRequest& data) override;      State GetCurrentState() const; @@ -61,8 +70,9 @@ private:      static constexpr std::size_t MifareSize = 0x400;      std::string file_path{}; -    State state{State::Initialized}; +    State state{State::Disabled};      std::vector<u8> nfc_data; +    Common::Input::NfcStatus status;      Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive};  };  } // namespace InputCommon diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 95106f16d..2c8c66951 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -2,6 +2,7 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include "common/logging/log.h" +#include "common/scope_exit.h"  #include "common/swap.h"  #include "common/thread.h"  #include "input_common/helpers/joycon_driver.h" @@ -112,7 +113,7 @@ DriverResult JoyconDriver::InitializeDevice() {      joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,                                                     right_stick_calibration, motion_calibration); -    // Start pooling for data +    // Start polling for data      is_connected = true;      if (!input_thread_running) {          input_thread = @@ -208,7 +209,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {          joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());      } -    if (nfc_protocol->IsEnabled()) { +    if (nfc_protocol->IsPolling()) {          if (amiibo_detected) {              if (!nfc_protocol->HasAmiibo()) {                  joycon_poller->UpdateAmiibo({}); @@ -218,10 +219,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {          }          if (!amiibo_detected) { -            std::vector<u8> data(0x21C); -            const auto result = nfc_protocol->ScanAmiibo(data); +            Joycon::TagInfo tag_info; +            const auto result = nfc_protocol->GetTagInfo(tag_info);              if (result == DriverResult::Success) { -                joycon_poller->UpdateAmiibo(data); +                joycon_poller->UpdateAmiibo(tag_info);                  amiibo_detected = true;              }          } @@ -247,6 +248,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {  }  DriverResult JoyconDriver::SetPollingMode() { +    SCOPE_EXIT({ disable_input_thread = false; });      disable_input_thread = true;      rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); @@ -276,7 +278,6 @@ DriverResult JoyconDriver::SetPollingMode() {      if (irs_enabled && supported_features.irs) {          auto result = irs_protocol->EnableIrs();          if (result == DriverResult::Success) { -            disable_input_thread = false;              return result;          }          irs_protocol->DisableIrs(); @@ -286,10 +287,6 @@ DriverResult JoyconDriver::SetPollingMode() {      if (nfc_enabled && supported_features.nfc) {          auto result = nfc_protocol->EnableNfc();          if (result == DriverResult::Success) { -            result = nfc_protocol->StartNFCPollingMode(); -        } -        if (result == DriverResult::Success) { -            disable_input_thread = false;              return result;          }          nfc_protocol->DisableNfc(); @@ -303,7 +300,6 @@ DriverResult JoyconDriver::SetPollingMode() {          }          if (result == DriverResult::Success) {              ring_connected = true; -            disable_input_thread = false;              return result;          }          ring_connected = false; @@ -314,7 +310,6 @@ DriverResult JoyconDriver::SetPollingMode() {      if (passive_enabled && supported_features.passive) {          const auto result = generic_protocol->EnablePassiveMode();          if (result == DriverResult::Success) { -            disable_input_thread = false;              return result;          }          LOG_ERROR(Input, "Error enabling passive mode"); @@ -328,7 +323,6 @@ DriverResult JoyconDriver::SetPollingMode() {      // Switch calls this function after enabling active mode      generic_protocol->TriggersElapsed(); -    disable_input_thread = false;      return result;  } @@ -492,9 +486,63 @@ DriverResult JoyconDriver::SetRingConMode() {      return result;  } -DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { +DriverResult JoyconDriver::StartNfcPolling() {      std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->StartNFCPollingMode(); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::StopNfcPolling() { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->StopNFCPollingMode(); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } +    if (!amiibo_detected) { +        return DriverResult::ErrorWritingData; +    } + +    out_data.resize(0x21C);      disable_input_thread = true; +    const auto result = nfc_protocol->ReadAmiibo(out_data); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { +    std::scoped_lock lock{mutex};      if (!supported_features.nfc) {          return DriverResult::NotSupported; @@ -506,9 +554,51 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {          return DriverResult::ErrorWritingData;      } +    disable_input_thread = true;      const auto result = nfc_protocol->WriteAmiibo(data); +    disable_input_thread = false; +    return result; +} + +DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data, +                                          std::span<MifareReadData> out_data) { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } +    if (!amiibo_detected) { +        return DriverResult::ErrorWritingData; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->ReadMifare(data, out_data); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } +    if (!amiibo_detected) { +        return DriverResult::ErrorWritingData; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->WriteMifare(data);      disable_input_thread = false; +      return result;  } diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index e9b2fccbb..bc7025a21 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -49,7 +49,13 @@ public:      DriverResult SetIrMode();      DriverResult SetNfcMode();      DriverResult SetRingConMode(); +    DriverResult StartNfcPolling(); +    DriverResult StopNfcPolling(); +    DriverResult ReadAmiiboData(std::vector<u8>& out_data);      DriverResult WriteNfcData(std::span<const u8> data); +    DriverResult ReadMifareData(std::span<const MifareReadChunk> request, +                                std::span<MifareReadData> out_data); +    DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);      void SetCallbacks(const JoyconCallbacks& callbacks); diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 5007b0e18..e0e431156 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -24,6 +24,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x  using MacAddress = std::array<u8, 6>;  using SerialNumber = std::array<u8, 15>;  using TagUUID = std::array<u8, 7>; +using MifareUUID = std::array<u8, 4>;  enum class ControllerType : u8 {      None = 0x00, @@ -307,6 +308,19 @@ enum class NFCStatus : u8 {      WriteDone = 0x05,      TagLost = 0x07,      WriteReady = 0x09, +    MifareDone = 0x10, +}; + +enum class MifareCmd : u8 { +    None = 0x00, +    Read = 0x30, +    AuthA = 0x60, +    AuthB = 0x61, +    Write = 0xA0, +    Transfer = 0xB0, +    Decrement = 0xC0, +    Increment = 0xC1, +    Store = 0xC2  };  enum class IrsMode : u8 { @@ -592,6 +606,14 @@ struct NFCWriteCommandData {  static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");  #pragma pack(pop) +struct MifareCommandData { +    u8 unknown1; +    u8 unknown2; +    u8 number_of_short_bytes; +    MifareUUID uid; +}; +static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size"); +  struct NFCPollingCommandData {      u8 enable_mifare;      u8 unknown_1; @@ -629,6 +651,41 @@ struct NFCWritePackage {      std::array<NFCDataChunk, 4> data_chunks;  }; +struct MifareReadChunk { +    MifareCmd command; +    std::array<u8, 0x6> sector_key; +    u8 sector; +}; + +struct MifareWriteChunk { +    MifareCmd command; +    std::array<u8, 0x6> sector_key; +    u8 sector; +    std::array<u8, 0x10> data; +}; + +struct MifareReadData { +    u8 sector; +    std::array<u8, 0x10> data; +}; + +struct MifareReadPackage { +    MifareCommandData command_data; +    std::array<MifareReadChunk, 0x10> data_chunks; +}; + +struct MifareWritePackage { +    MifareCommandData command_data; +    std::array<MifareWriteChunk, 0x10> data_chunks; +}; + +struct TagInfo { +    u8 uuid_length; +    u8 protocol; +    u8 tag_type; +    std::array<u8, 10> uuid; +}; +  struct IrsConfigure {      MCUCommand command;      MCUSubCommand sub_command; @@ -744,7 +801,7 @@ struct JoyconCallbacks {      std::function<void(int, f32)> on_stick_data;      std::function<void(int, const MotionData&)> on_motion_data;      std::function<void(f32)> on_ring_data; -    std::function<void(const std::vector<u8>&)> on_amiibo_data; +    std::function<void(const Joycon::TagInfo&)> on_amiibo_data;      std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;  }; diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index f7058c4a7..261f46255 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -40,6 +40,16 @@ DriverResult NfcProtocol::EnableNfc() {      if (result == DriverResult::Success) {          result = WaitUntilNfcIs(NFCStatus::Ready);      } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    } +    if (result == DriverResult::Success) { +        is_enabled = true; +    }      return result;  } @@ -54,37 +64,50 @@ DriverResult NfcProtocol::DisableNfc() {      }      is_enabled = false; +    is_polling = false;      return result;  }  DriverResult NfcProtocol::StartNFCPollingMode() { -    LOG_DEBUG(Input, "Start NFC pooling Mode"); +    LOG_DEBUG(Input, "Start NFC polling Mode");      ScopedSetBlocking sb(this);      DriverResult result{DriverResult::Success};      if (result == DriverResult::Success) {          MCUCommandResponse output{}; -        result = SendStopPollingRequest(output); +        result = SendStartPollingRequest(output);      }      if (result == DriverResult::Success) { -        result = WaitUntilNfcIs(NFCStatus::Ready); +        result = WaitUntilNfcIs(NFCStatus::Polling);      }      if (result == DriverResult::Success) { +        is_polling = true; +    } + +    return result; +} + +DriverResult NfcProtocol::StopNFCPollingMode() { +    LOG_DEBUG(Input, "Stop NFC polling Mode"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; + +    if (result == DriverResult::Success) {          MCUCommandResponse output{}; -        result = SendStartPollingRequest(output); +        result = SendStopPollingRequest(output);      }      if (result == DriverResult::Success) { -        result = WaitUntilNfcIs(NFCStatus::Polling); +        result = WaitUntilNfcIs(NFCStatus::WriteReady);      }      if (result == DriverResult::Success) { -        is_enabled = true; +        is_polling = false;      }      return result;  } -DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { +DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {      if (update_counter++ < AMIIBO_UPDATE_DELAY) {          return DriverResult::Delayed;      } @@ -100,11 +123,41 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {      }      if (result == DriverResult::Success) { +        tag_info = { +            .uuid_length = tag_data.uuid_size, +            .protocol = 1, +            .tag_type = tag_data.type, +            .uuid = {}, +        }; + +        memcpy(tag_info.uuid.data(), tag_data.uuid.data(), tag_data.uuid_size); + +        // Investigate why mifare type is not correct +        if (tag_info.tag_type == 144) { +            tag_info.tag_type = 1U << 6; +        } +          std::string uuid_string;          for (auto& content : tag_data.uuid) {              uuid_string += fmt::format(" {:02x}", content);          }          LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string); +    } + +    return result; +} + +DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) { +    LOG_DEBUG(Input, "Scan for amiibos"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; +    TagFoundData tag_data{}; + +    if (result == DriverResult::Success) { +        result = IsTagInRange(tag_data, 7); +    } + +    if (result == DriverResult::Success) {          result = GetAmiiboData(data);      } @@ -154,6 +207,69 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {      return result;  } +DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request, +                                     std::span<MifareReadData> out_data) { +    LOG_DEBUG(Input, "Read mifare"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; +    TagFoundData tag_data{}; +    MifareUUID tag_uuid{}; + +    if (result == DriverResult::Success) { +        result = IsTagInRange(tag_data, 7); +    } +    if (result == DriverResult::Success) { +        memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); +        result = GetMifareData(tag_uuid, read_request, out_data); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStartPollingRequest(output, true); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::WriteReady); +    } +    return result; +} + +DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) { +    LOG_DEBUG(Input, "Write mifare"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; +    TagFoundData tag_data{}; +    MifareUUID tag_uuid{}; + +    if (result == DriverResult::Success) { +        result = IsTagInRange(tag_data, 7); +    } +    if (result == DriverResult::Success) { +        memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); +        result = WriteMifareData(tag_uuid, write_request); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStartPollingRequest(output, true); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::WriteReady); +    } +    return result; +} +  bool NfcProtocol::HasAmiibo() {      if (update_counter++ < AMIIBO_UPDATE_DELAY) {          return true; @@ -341,6 +457,158 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con      return result;  } +DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid, +                                        std::span<const MifareReadChunk> read_request, +                                        std::span<MifareReadData> out_data) { +    constexpr std::size_t timeout_limit = 60; +    const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request); +    const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data); +    std::span<const u8> buffer(nfc_buffer_data); +    DriverResult result = DriverResult::Success; +    MCUCommandResponse output{}; +    u8 block_id = 1; +    u8 package_index = 0; +    std::size_t tries = 0; +    std::size_t current_position = 0; + +    LOG_INFO(Input, "Reading Mifare data"); + +    // Send data request. Nfc buffer size is 31, Send the data in smaller packages +    while (current_position < buffer.size() && tries++ < timeout_limit) { +        const std::size_t next_position = +            std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); +        const std::size_t block_size = next_position - current_position; +        const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); + +        SendReadDataMifareRequest(output, block_id, is_last_packet, +                                  buffer.subspan(current_position, block_size)); + +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        // Increase position when data is confirmed by the joycon +        if (output.mcu_report == MCUReport::NFCState && +            (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && +            output.mcu_data[3] == block_id) { +            block_id++; +            current_position = next_position; +        } +    } + +    if (result != DriverResult::Success) { +        return result; +    } + +    // Wait for reply and save the output data +    while (tries++ < timeout_limit) { +        result = SendNextPackageRequest(output, package_index); +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (result != DriverResult::Success) { +            return result; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { +            constexpr std::size_t DATA_LENGHT = 0x10 + 1; +            constexpr std::size_t DATA_START = 11; +            const u8 number_of_elements = output.mcu_data[10]; +            for (std::size_t i = 0; i < number_of_elements; i++) { +                out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)]; +                memcpy(out_data[i].data.data(), +                       output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT), +                       sizeof(MifareReadData::data)); +            } +            package_index++; +            continue; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) { +            LOG_INFO(Input, "Finished reading mifare"); +            break; +        } +    } + +    return result; +} + +DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid, +                                          std::span<const MifareWriteChunk> write_request) { +    constexpr std::size_t timeout_limit = 60; +    const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request); +    const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data); +    std::span<const u8> buffer(nfc_buffer_data); +    DriverResult result = DriverResult::Success; +    MCUCommandResponse output{}; +    u8 block_id = 1; +    u8 package_index = 0; +    std::size_t tries = 0; +    std::size_t current_position = 0; + +    LOG_INFO(Input, "Writing Mifare data"); + +    // Send data request. Nfc buffer size is 31, Send the data in smaller packages +    while (current_position < buffer.size() && tries++ < timeout_limit) { +        const std::size_t next_position = +            std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); +        const std::size_t block_size = next_position - current_position; +        const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); + +        SendReadDataMifareRequest(output, block_id, is_last_packet, +                                  buffer.subspan(current_position, block_size)); + +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        // Increase position when data is confirmed by the joycon +        if (output.mcu_report == MCUReport::NFCState && +            (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && +            output.mcu_data[3] == block_id) { +            block_id++; +            current_position = next_position; +        } +    } + +    if (result != DriverResult::Success) { +        return result; +    } + +    // Wait for reply and ignore the output data +    while (tries++ < timeout_limit) { +        result = SendNextPackageRequest(output, package_index); +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (result != DriverResult::Success) { +            return result; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { +            package_index++; +            continue; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) { +            LOG_INFO(Input, "Finished writing mifare"); +            break; +        } +    } + +    return result; +} +  DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,                                                    bool is_second_attempt) {      NFCRequestState request{ @@ -477,6 +745,28 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,                         output);  } +DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, +                                                    bool is_last_packet, std::span<const u8> data) { +    const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); +    NFCRequestState request{ +        .command_argument = NFCCommand::Mifare, +        .block_id = block_id, +        .packet_id = {}, +        .packet_flag = +            is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining, +        .data_length = static_cast<u8>(data_size), +        .raw_data = {}, +        .crc = {}, +    }; +    memcpy(request.raw_data.data(), data.data(), data_size); + +    std::array<u8, sizeof(NFCRequestState)> request_data{}; +    memcpy(request_data.data(), &request, sizeof(NFCRequestState)); +    request_data[36] = CalculateMCU_CRC8(request_data.data(), 36); +    return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data, +                       output); +} +  std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {      const std::size_t header_size =          sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); @@ -498,6 +788,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag      return serialized_data;  } +std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const { +    const std::size_t header_size = sizeof(MifareCommandData); +    std::vector<u8> serialized_data(header_size); +    std::size_t start_index = 0; + +    memcpy(serialized_data.data(), &package, header_size); +    start_index += header_size; + +    for (const auto& data_chunk : package.data_chunks) { +        const std::size_t chunk_size = sizeof(MifareReadChunk); +        if (data_chunk.command == MifareCmd::None) { +            continue; +        } +        serialized_data.resize(start_index + chunk_size); +        memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); +        start_index += chunk_size; +    } + +    return serialized_data; +} + +std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const { +    const std::size_t header_size = sizeof(MifareCommandData); +    std::vector<u8> serialized_data(header_size); +    std::size_t start_index = 0; + +    memcpy(serialized_data.data(), &package, header_size); +    start_index += header_size; + +    for (const auto& data_chunk : package.data_chunks) { +        const std::size_t chunk_size = sizeof(MifareWriteChunk); +        if (data_chunk.command == MifareCmd::None) { +            continue; +        } +        serialized_data.resize(start_index + chunk_size); +        memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); +        start_index += chunk_size; +    } + +    return serialized_data; +} +  NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,                                                      std::span<const u8> data) const {      return { @@ -527,6 +859,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,      };  } +MifareReadPackage NfcProtocol::MakeMifareReadPackage( +    const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const { +    MifareReadPackage package{ +        .command_data{ +            .unknown1 = 0xd0, +            .unknown2 = 0x07, +            .number_of_short_bytes = static_cast<u8>( +                ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2), +            .uid = tag_uuid, +        }, +        .data_chunks = {}, +    }; + +    for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) { +        package.data_chunks[i] = read_request[i]; +    } + +    return package; +} + +MifareWritePackage NfcProtocol::MakeMifareWritePackage( +    const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const { +    MifareWritePackage package{ +        .command_data{ +            .unknown1 = 0xd0, +            .unknown2 = 0x07, +            .number_of_short_bytes = static_cast<u8>( +                ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2), +            .uid = tag_uuid, +        }, +        .data_chunks = {}, +    }; + +    for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) { +        package.data_chunks[i] = read_request[i]; +    } + +    return package; +} +  NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {      constexpr u8 NFC_PAGE_SIZE = 4; @@ -606,4 +978,8 @@ bool NfcProtocol::IsEnabled() const {      return is_enabled;  } +bool NfcProtocol::IsPolling() const { +    return is_polling; +} +  } // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index eb58c427d..0be95e40e 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -25,14 +25,25 @@ public:      DriverResult StartNFCPollingMode(); -    DriverResult ScanAmiibo(std::vector<u8>& data); +    DriverResult StopNFCPollingMode(); + +    DriverResult GetTagInfo(Joycon::TagInfo& tag_info); + +    DriverResult ReadAmiibo(std::vector<u8>& data);      DriverResult WriteAmiibo(std::span<const u8> data); +    DriverResult ReadMifare(std::span<const MifareReadChunk> read_request, +                            std::span<MifareReadData> out_data); + +    DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request); +      bool HasAmiibo();      bool IsEnabled() const; +    bool IsPolling() const; +  private:      // Number of times the function will be delayed until it outputs valid data      static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15; @@ -51,6 +62,13 @@ private:      DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); +    DriverResult GetMifareData(const MifareUUID& tag_uuid, +                               std::span<const MifareReadChunk> read_request, +                               std::span<MifareReadData> out_data); + +    DriverResult WriteMifareData(const MifareUUID& tag_uuid, +                                 std::span<const MifareWriteChunk> write_request); +      DriverResult SendStartPollingRequest(MCUCommandResponse& output,                                           bool is_second_attempt = false); @@ -65,17 +83,31 @@ private:      DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,                                              bool is_last_packet, std::span<const u8> data); +    DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, +                                           bool is_last_packet, std::span<const u8> data); +      std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; +    std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const; + +    std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const; +      NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;      NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; +    MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid, +                                            std::span<const MifareReadChunk> read_request) const; + +    MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid, +                                              std::span<const MifareWriteChunk> read_request) const; +      NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;      TagUUID GetTagUUID(std::span<const u8> data) const;      bool is_enabled{}; +    bool is_polling{};      std::size_t update_counter{};  }; diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index dca797f7a..1aab9e12a 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -70,8 +70,8 @@ void JoyconPoller::UpdateColor(const Color& color) {      callbacks.on_color_data(color);  } -void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { -    callbacks.on_amiibo_data(amiibo_data); +void JoyconPoller::UpdateAmiibo(const Joycon::TagInfo& tag_info) { +    callbacks.on_amiibo_data(tag_info);  }  void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h index 0fa72c6db..3746abe5d 100644 --- a/src/input_common/helpers/joycon_protocol/poller.h +++ b/src/input_common/helpers/joycon_protocol/poller.h @@ -36,8 +36,8 @@ public:      void UpdateColor(const Color& color);      void UpdateRing(s16 value, const RingStatus& ring_status); -    void UpdateAmiibo(const std::vector<u8>& amiibo_data); -    void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); +    void UpdateAmiibo(const Joycon::TagInfo& tag_info); +    void UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format);  private:      void UpdateActiveLeftPadInput(const InputReportActive& input, diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 50b5a3dc8..c2d0cbb34 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -143,12 +143,46 @@ public:          return Common::Input::NfcState::NotSupported;      } +    // Start scanning for nfc tags +    virtual Common::Input::NfcState StartNfcPolling( +        [[maybe_unused]] const PadIdentifier& identifier_) { +        return Common::Input::NfcState::NotSupported; +    } + +    // Start scanning for nfc tags +    virtual Common::Input::NfcState StopNfcPolling( +        [[maybe_unused]] const PadIdentifier& identifier_) { +        return Common::Input::NfcState::NotSupported; +    } + +    // Reads data from amiibo tag +    virtual Common::Input::NfcState ReadAmiiboData( +        [[maybe_unused]] const PadIdentifier& identifier_, +        [[maybe_unused]] std::vector<u8>& out_data) { +        return Common::Input::NfcState::NotSupported; +    } +      // Writes data to an nfc tag      virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,                                                   [[maybe_unused]] const std::vector<u8>& data) {          return Common::Input::NfcState::NotSupported;      } +    // Reads data from mifare tag +    virtual Common::Input::NfcState ReadMifareData( +        [[maybe_unused]] const PadIdentifier& identifier_, +        [[maybe_unused]] const Common::Input::MifareRequest& request, +        [[maybe_unused]] Common::Input::MifareRequest& out_data) { +        return Common::Input::NfcState::NotSupported; +    } + +    // Write data to mifare tag +    virtual Common::Input::NfcState WriteMifareData( +        [[maybe_unused]] const PadIdentifier& identifier_, +        [[maybe_unused]] const Common::Input::MifareRequest& request) { +        return Common::Input::NfcState::NotSupported; +    } +      // Returns the engine name      [[nodiscard]] const std::string& GetEngineName() const; diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 380a01542..870e76ab0 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -792,8 +792,7 @@ public:          const Common::Input::CallbackStatus status{              .type = Common::Input::InputType::Nfc, -            .nfc_status = nfc_status.state, -            .raw_data = nfc_status.data, +            .nfc_status = nfc_status,          };          TriggerOnChange(status); @@ -836,10 +835,31 @@ public:          return input_engine->SupportsNfc(identifier);      } +    Common::Input::NfcState StartNfcPolling() { +        return input_engine->StartNfcPolling(identifier); +    } + +    Common::Input::NfcState StopNfcPolling() { +        return input_engine->StopNfcPolling(identifier); +    } + +    Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) { +        return input_engine->ReadAmiiboData(identifier, out_data); +    } +      Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {          return input_engine->WriteNfcData(identifier, data);      } +    Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, +                                           Common::Input::MifareRequest& out_data) { +        return input_engine->ReadMifareData(identifier, request, out_data); +    } + +    Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) { +        return input_engine->WriteMifareData(identifier, request); +    } +  private:      const PadIdentifier identifier;      InputEngine* input_engine; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 013715b44..cba7c3cce 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3836,7 +3836,7 @@ void GMainWindow::OnLoadAmiibo() {      auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();      // Remove amiibo if one is connected -    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { +    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {          virtual_amiibo->CloseAmiibo();          QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));          return; @@ -3864,7 +3864,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {      auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();      const QString title = tr("Error loading Amiibo data");      // Remove amiibo if one is connected -    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { +    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {          virtual_amiibo->CloseAmiibo();          QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));          return; | 
