summaryrefslogtreecommitdiffstats
path: root/src/input_common
diff options
context:
space:
mode:
authorliamwhite <liamwhite@users.noreply.github.com>2023-06-23 15:27:00 +0200
committerGitHub <noreply@github.com>2023-06-23 15:27:00 +0200
commit87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77 (patch)
tree4239b5df2c866aa26e123719fe838bd6eb0e0394 /src/input_common
parentMerge pull request #10884 from liamwhite/spaghetti-vfs (diff)
parentinput_common: Implement native mifare support (diff)
downloadyuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar.gz
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar.bz2
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar.lz
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar.xz
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar.zst
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.zip
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/drivers/joycon.cpp133
-rw-r--r--src/input_common/drivers/joycon.h18
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp130
-rw-r--r--src/input_common/drivers/virtual_amiibo.h16
-rw-r--r--src/input_common/helpers/joycon_driver.cpp118
-rw-r--r--src/input_common/helpers/joycon_driver.h6
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h59
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp390
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h34
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp4
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.h4
-rw-r--r--src/input_common/input_engine.h34
-rw-r--r--src/input_common/input_poller.cpp24
13 files changed, 915 insertions, 55 deletions
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;