summaryrefslogtreecommitdiffstats
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/input_common/drivers/joycon.cpp17
-rw-r--r--src/input_common/drivers/joycon.h3
-rw-r--r--src/input_common/helpers/joycon_driver.cpp20
-rw-r--r--src/input_common/helpers/joycon_driver.h1
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp2
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h50
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp397
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h31
-rw-r--r--src/input_common/input_engine.cpp11
9 files changed, 408 insertions, 124 deletions
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 8b57ebe07..b2b5677c8 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](const std::vector<u8>& amiibo_data) {
- OnAmiiboUpdate(port, amiibo_data);
+ .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) {
+ OnAmiiboUpdate(port, type, amiibo_data);
}},
.on_camera_data = {[this, port](const std::vector<u8>& camera_data,
Joycon::IrsResolution format) {
@@ -291,9 +291,13 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c
return Common::Input::NfcState::Success;
};
-Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
+Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
const std::vector<u8>& data) {
- return Common::Input::NfcState::NotSupported;
+ auto handle = GetHandle(identifier);
+ if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) {
+ return Common::Input::NfcState::WriteFailed;
+ }
+ return Common::Input::NfcState::Success;
};
Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
@@ -398,8 +402,9 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
SetAxis(identifier, 100, ring_data);
}
-void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
- const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
+void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
+ const std::vector<u8>& amiibo_data) {
+ 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});
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index 5b40817e2..e3f0ad78f 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -81,7 +81,8 @@ private:
void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
const Joycon::MotionData& value);
void OnRingConUpdate(f32 ring_data);
- void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
+ void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
+ const std::vector<u8>& amiibo_data);
void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
Joycon::IrsResolution format);
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 83429a336..95106f16d 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -492,6 +492,26 @@ DriverResult JoyconDriver::SetRingConMode() {
return result;
}
+DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
+ std::scoped_lock lock{mutex};
+ disable_input_thread = true;
+
+ if (!supported_features.nfc) {
+ return DriverResult::NotSupported;
+ }
+ if (!nfc_protocol->IsEnabled()) {
+ return DriverResult::Disabled;
+ }
+ if (!amiibo_detected) {
+ return DriverResult::ErrorWritingData;
+ }
+
+ const auto result = nfc_protocol->WriteAmiibo(data);
+
+ disable_input_thread = false;
+ return result;
+}
+
bool JoyconDriver::IsConnected() const {
std::scoped_lock lock{mutex};
return is_connected.load();
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
index 72a9e71dc..e9b2fccbb 100644
--- a/src/input_common/helpers/joycon_driver.h
+++ b/src/input_common/helpers/joycon_driver.h
@@ -49,6 +49,7 @@ public:
DriverResult SetIrMode();
DriverResult SetNfcMode();
DriverResult SetRingConMode();
+ DriverResult WriteNfcData(std::span<const u8> data);
void SetCallbacks(const JoyconCallbacks& callbacks);
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 077d72cd0..51669261a 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -265,7 +265,7 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCom
DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
MCUCommandResponse output{};
- constexpr std::size_t MaxTries{8};
+ constexpr std::size_t MaxTries{16};
std::size_t tries{};
do {
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 1c8d294b0..5007b0e18 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -23,6 +23,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>;
enum class ControllerType : u8 {
None = 0x00,
@@ -276,12 +277,13 @@ enum class MCUPacketFlag : u8 {
LastCommandPacket = 0x08,
};
-enum class NFCReadCommand : u8 {
+enum class NFCCommand : u8 {
CancelAll = 0x00,
StartPolling = 0x01,
StopPolling = 0x02,
StartWaitingRecieve = 0x04,
- Ntag = 0x06,
+ ReadNtag = 0x06,
+ WriteNtag = 0x08,
Mifare = 0x0F,
};
@@ -292,14 +294,19 @@ enum class NFCTagType : u8 {
enum class NFCPages {
Block0 = 0,
+ Block3 = 3,
Block45 = 45,
Block135 = 135,
Block231 = 231,
};
enum class NFCStatus : u8 {
+ Ready = 0x00,
+ Polling = 0x01,
LastPackage = 0x04,
+ WriteDone = 0x05,
TagLost = 0x07,
+ WriteReady = 0x09,
};
enum class IrsMode : u8 {
@@ -559,13 +566,32 @@ static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an inv
struct NFCReadCommandData {
u8 unknown;
u8 uuid_length;
- u8 unknown_2;
- std::array<u8, 6> uid;
+ TagUUID uid;
NFCTagType tag_type;
NFCReadBlockCommand read_block;
};
static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
+#pragma pack(push, 1)
+struct NFCWriteCommandData {
+ u8 unknown;
+ u8 uuid_length;
+ TagUUID uid;
+ NFCTagType tag_type;
+ u8 unknown2;
+ u8 unknown3;
+ u8 unknown4;
+ u8 unknown5;
+ u8 unknown6;
+ u8 unknown7;
+ u8 unknown8;
+ u8 magic;
+ u16_be write_count;
+ u8 amiibo_version;
+};
+static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
+#pragma pack(pop)
+
struct NFCPollingCommandData {
u8 enable_mifare;
u8 unknown_1;
@@ -576,9 +602,9 @@ struct NFCPollingCommandData {
static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
struct NFCRequestState {
- NFCReadCommand command_argument;
+ NFCCommand command_argument;
+ u8 block_id;
u8 packet_id;
- INSERT_PADDING_BYTES(0x1);
MCUPacketFlag packet_flag;
u8 data_length;
union {
@@ -591,6 +617,18 @@ struct NFCRequestState {
};
static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
+struct NFCDataChunk {
+ u8 nfc_page;
+ u8 data_size;
+ std::array<u8, 0xFF> data;
+};
+
+struct NFCWritePackage {
+ NFCWriteCommandData command_data;
+ u8 number_of_chunks;
+ std::array<NFCDataChunk, 4> data_chunks;
+};
+
struct IrsConfigure {
MCUCommand command;
MCUSubCommand sub_command;
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index 14818ae33..3b7a628e5 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -34,6 +34,12 @@ DriverResult NfcProtocol::EnableNfc() {
result = ConfigureMCU(config);
}
+ if (result == DriverResult::Success) {
+ result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitUntilNfcIs(NFCStatus::Ready);
+ }
return result;
}
@@ -56,13 +62,20 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
LOG_DEBUG(Input, "Start NFC pooling Mode");
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
- TagFoundData tag_data{};
if (result == DriverResult::Success) {
- result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
+ MCUCommandResponse output{};
+ result = SendStopPollingRequest(output);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitUntilNfcIs(NFCStatus::Ready);
+ }
+ if (result == DriverResult::Success) {
+ MCUCommandResponse output{};
+ result = SendStartPollingRequest(output);
}
if (result == DriverResult::Success) {
- result = WaitUntilNfcIsReady();
+ result = WaitUntilNfcIs(NFCStatus::Polling);
}
if (result == DriverResult::Success) {
is_enabled = true;
@@ -77,49 +90,94 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
}
update_counter = 0;
- LOG_DEBUG(Input, "Start NFC pooling Mode");
+ LOG_DEBUG(Input, "Scan for amiibos");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ TagFoundData tag_data{};
+
+ if (result == DriverResult::Success) {
+ result = IsTagInRange(tag_data);
+ }
+
+ if (result == DriverResult::Success) {
+ 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);
+ result = GetAmiiboData(data);
+ }
+
+ return result;
+}
+
+DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
+ LOG_DEBUG(Input, "Write amiibo");
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
+ TagUUID tag_uuid = GetTagUUID(data);
TagFoundData tag_data{};
if (result == DriverResult::Success) {
- result = StartPolling(tag_data);
+ result = IsTagInRange(tag_data, 7);
}
if (result == DriverResult::Success) {
- result = ReadTag(tag_data);
+ if (tag_data.uuid != tag_uuid) {
+ result = DriverResult::InvalidParameters;
+ }
}
if (result == DriverResult::Success) {
- result = WaitUntilNfcIsReady();
+ MCUCommandResponse output{};
+ result = SendStopPollingRequest(output);
}
if (result == DriverResult::Success) {
- result = StartPolling(tag_data, 7);
+ result = WaitUntilNfcIs(NFCStatus::Ready);
}
if (result == DriverResult::Success) {
- result = GetAmiiboData(data);
+ MCUCommandResponse output{};
+ result = SendStartPollingRequest(output, true);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitUntilNfcIs(NFCStatus::WriteReady);
+ }
+ if (result == DriverResult::Success) {
+ result = WriteAmiiboData(tag_uuid, data);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitUntilNfcIs(NFCStatus::WriteDone);
+ }
+ if (result == DriverResult::Success) {
+ MCUCommandResponse output{};
+ result = SendStopPollingRequest(output);
}
return result;
}
bool NfcProtocol::HasAmiibo() {
+ if (update_counter++ < AMIIBO_UPDATE_DELAY) {
+ return true;
+ }
+ update_counter = 0;
+
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
TagFoundData tag_data{};
if (result == DriverResult::Success) {
- result = StartPolling(tag_data);
+ result = IsTagInRange(tag_data, 7);
}
return result == DriverResult::Success;
}
-DriverResult NfcProtocol::WaitUntilNfcIsReady() {
+DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
constexpr std::size_t timeout_limit = 10;
MCUCommandResponse output{};
std::size_t tries = 0;
do {
- auto result = SendStartWaitingRecieveRequest(output);
+ auto result = SendNextPackageRequest(output, {});
if (result != DriverResult::Success) {
return result;
@@ -129,18 +187,17 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
}
} while (output.mcu_report != MCUReport::NFCState ||
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
- output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
+ output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));
return DriverResult::Success;
}
-DriverResult NfcProtocol::StartPolling(TagFoundData& data, std::size_t timeout_limit) {
- LOG_DEBUG(Input, "Start Polling for tag");
+DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_limit) {
MCUCommandResponse output{};
std::size_t tries = 0;
do {
- const auto result = SendStartPollingRequest(output);
+ const auto result = SendNextPackageRequest(output, {});
if (result != DriverResult::Success) {
return result;
}
@@ -149,32 +206,31 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data, std::size_t timeout_l
}
} while (output.mcu_report != MCUReport::NFCState ||
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
- output.mcu_data[6] != 0x09);
+ (output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04));
data.type = output.mcu_data[12];
- data.uuid.resize(output.mcu_data[14]);
+ data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID)));
memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
return DriverResult::Success;
}
-DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
- constexpr std::size_t timeout_limit = 10;
+DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
+ constexpr std::size_t timeout_limit = 60;
MCUCommandResponse output{};
std::size_t tries = 0;
- std::string uuid_string;
- for (auto& content : data.uuid) {
- uuid_string += fmt::format(" {:02x}", content);
- }
+ u8 package_index = 0;
+ std::size_t ntag_buffer_pos = 0;
+ auto result = SendReadAmiiboRequest(output, NFCPages::Block135);
- LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
+ if (result != DriverResult::Success) {
+ return result;
+ }
- tries = 0;
- NFCPages ntag_pages = NFCPages::Block0;
// Read Tag data
- while (true) {
- auto result = SendReadAmiiboRequest(output, ntag_pages);
+ while (tries++ < timeout_limit) {
+ result = SendNextPackageRequest(output, package_index);
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
if (result != DriverResult::Success) {
@@ -187,56 +243,51 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
return DriverResult::ErrorReadingData;
}
- if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
- output.mcu_data[2] == 0x01) {
- if (data.type != 2) {
- continue;
- }
- switch (output.mcu_data[24]) {
- case 0:
- ntag_pages = NFCPages::Block135;
- break;
- case 3:
- ntag_pages = NFCPages::Block45;
- break;
- case 4:
- ntag_pages = NFCPages::Block231;
- break;
- default:
- return DriverResult::ErrorReadingData;
+ if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
+ std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
+ if (output.mcu_data[2] == 0x01) {
+ memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
+ payload_size - 60);
+ ntag_buffer_pos += payload_size - 60;
+ } else {
+ memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
+ payload_size);
}
+ package_index++;
continue;
}
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
- // finished
- SendStopPollingRequest(output);
+ LOG_INFO(Input, "Finished reading amiibo");
return DriverResult::Success;
}
-
- // Ignore other state reports
- if (output.mcu_report == MCUReport::NFCState) {
- continue;
- }
-
- if (tries++ > timeout_limit) {
- return DriverResult::Timeout;
- }
}
- return DriverResult::Success;
+ return DriverResult::Timeout;
}
-DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
- constexpr std::size_t timeout_limit = 10;
+DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) {
+ constexpr std::size_t timeout_limit = 60;
+ const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data);
+ const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data);
+ std::span<const u8> buffer(nfc_buffer_data);
MCUCommandResponse output{};
+ u8 block_id = 1;
+ u8 package_index = 0;
std::size_t tries = 0;
+ std::size_t current_position = 0;
- NFCPages ntag_pages = NFCPages::Block135;
- std::size_t ntag_buffer_pos = 0;
- // Read Tag data
- while (true) {
- auto result = SendReadAmiiboRequest(output, ntag_pages);
+ LOG_INFO(Input, "Writing amiibo data");
+
+ auto result = SendWriteAmiiboRequest(output, tag_uuid);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ // Read Tag data but ignore the actual sent 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) {
@@ -250,47 +301,59 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
}
if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
- std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
- if (output.mcu_data[2] == 0x01) {
- memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
- payload_size - 60);
- ntag_buffer_pos += payload_size - 60;
- } else {
- memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
- payload_size);
- }
+ package_index++;
continue;
}
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
LOG_INFO(Input, "Finished reading amiibo");
- return DriverResult::Success;
+ break;
}
+ }
- // Ignore other state reports
- if (output.mcu_report == MCUReport::NFCState) {
- continue;
+ // Send Data. 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);
+
+ SendWriteDataAmiiboRequest(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::NFCReadData ||
+ output.mcu_report == MCUReport::NFCState) &&
+ nfc_status == NFCStatus::TagLost) {
+ return DriverResult::ErrorReadingData;
}
- if (tries++ > timeout_limit) {
- return DriverResult::Timeout;
+ // 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;
}
}
- return DriverResult::Success;
+ return result;
}
-DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
+DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
+ bool is_second_attempt) {
NFCRequestState request{
- .command_argument = NFCReadCommand::StartPolling,
- .packet_id = 0x0,
+ .command_argument = NFCCommand::StartPolling,
+ .block_id = {},
+ .packet_id = {},
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = sizeof(NFCPollingCommandData),
.nfc_polling =
{
- .enable_mifare = 0x01,
- .unknown_1 = 0x00,
- .unknown_2 = 0x00,
+ .enable_mifare = 0x00,
+ .unknown_1 = static_cast<u8>(is_second_attempt ? 0xe8 : 0x00),
+ .unknown_2 = static_cast<u8>(is_second_attempt ? 0x03 : 0x00),
.unknown_3 = 0x2c,
.unknown_4 = 0x01,
},
@@ -306,10 +369,11 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
NFCRequestState request{
- .command_argument = NFCReadCommand::StopPolling,
- .packet_id = 0x0,
+ .command_argument = NFCCommand::StopPolling,
+ .block_id = {},
+ .packet_id = {},
.packet_flag = MCUPacketFlag::LastCommandPacket,
- .data_length = 0,
+ .data_length = {},
.raw_data = {},
.crc = {},
};
@@ -321,12 +385,13 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
output);
}
-DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
+DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {
NFCRequestState request{
- .command_argument = NFCReadCommand::StartWaitingRecieve,
- .packet_id = 0x0,
+ .command_argument = NFCCommand::StartWaitingRecieve,
+ .block_id = {},
+ .packet_id = packet_id,
.packet_flag = MCUPacketFlag::LastCommandPacket,
- .data_length = 0,
+ .data_length = {},
.raw_data = {},
.crc = {},
};
@@ -340,17 +405,17 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& out
DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
NFCRequestState request{
- .command_argument = NFCReadCommand::Ntag,
- .packet_id = 0x0,
+ .command_argument = NFCCommand::ReadNtag,
+ .block_id = {},
+ .packet_id = {},
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = sizeof(NFCReadCommandData),
.nfc_read =
{
.unknown = 0xd0,
- .uuid_length = 0x07,
- .unknown_2 = 0x00,
+ .uuid_length = sizeof(NFCReadCommandData::uid),
.uid = {},
- .tag_type = NFCTagType::AllTags,
+ .tag_type = NFCTagType::Ntag215,
.read_block = GetReadBlockCommand(ntag_pages),
},
.crc = {},
@@ -363,12 +428,135 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
output);
}
+DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output,
+ const TagUUID& tag_uuid) {
+ NFCRequestState request{
+ .command_argument = NFCCommand::ReadNtag,
+ .block_id = {},
+ .packet_id = {},
+ .packet_flag = MCUPacketFlag::LastCommandPacket,
+ .data_length = sizeof(NFCReadCommandData),
+ .nfc_read =
+ {
+ .unknown = 0xd0,
+ .uuid_length = sizeof(NFCReadCommandData::uid),
+ .uid = tag_uuid,
+ .tag_type = NFCTagType::Ntag215,
+ .read_block = GetReadBlockCommand(NFCPages::Block3),
+ },
+ .crc = {},
+ };
+
+ 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);
+}
+
+DriverResult NfcProtocol::SendWriteDataAmiiboRequest(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::WriteNtag,
+ .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);
+ 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(NFCDataChunk::nfc_page) + sizeof(NFCDataChunk::data_size) + data_chunk.data_size;
+
+ 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 {
+ .command_data{
+ .unknown = 0xd0,
+ .uuid_length = sizeof(NFCReadCommandData::uid),
+ .uid = tag_uuid,
+ .tag_type = NFCTagType::Ntag215,
+ .unknown2 = 0x00,
+ .unknown3 = 0x01,
+ .unknown4 = 0x04,
+ .unknown5 = 0xff,
+ .unknown6 = 0xff,
+ .unknown7 = 0xff,
+ .unknown8 = 0xff,
+ .magic = data[16],
+ .write_count = static_cast<u16>((data[17] << 8) + data[18]),
+ .amiibo_version = data[19],
+ },
+ .number_of_chunks = 3,
+ .data_chunks =
+ {
+ MakeAmiiboChunk(0x05, 0x20, data),
+ MakeAmiiboChunk(0x20, 0xf0, data),
+ MakeAmiiboChunk(0x5c, 0x98, data),
+ },
+ };
+}
+
+NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
+ constexpr u8 PAGE_SIZE = 4;
+
+ if (static_cast<std::size_t>(page * PAGE_SIZE) + size >= data.size()) {
+ return {};
+ }
+
+ NFCDataChunk chunk{
+ .nfc_page = page,
+ .data_size = size,
+ .data = {},
+ };
+ std::memcpy(chunk.data.data(), data.data() + (page * PAGE_SIZE), size);
+ return chunk;
+}
+
NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
switch (pages) {
case NFCPages::Block0:
return {
.block_count = 1,
};
+ case NFCPages::Block3:
+ return {
+ .block_count = 1,
+ .blocks =
+ {
+ NFCReadBlock{0x03, 0x03},
+ },
+ };
case NFCPages::Block45:
return {
.block_count = 1,
@@ -403,6 +591,17 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
};
}
+TagUUID NfcProtocol::GetTagUUID(std::span<const u8> data) const {
+ if (data.size() < 10) {
+ return {};
+ }
+
+ // crc byte 3 is omitted in this operation
+ return {
+ data[0], data[1], data[2], data[4], data[5], data[6], data[7],
+ };
+}
+
bool NfcProtocol::IsEnabled() const {
return is_enabled;
}
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index 4cb992d1d..eb58c427d 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -27,6 +27,8 @@ public:
DriverResult ScanAmiibo(std::vector<u8>& data);
+ DriverResult WriteAmiibo(std::span<const u8> data);
+
bool HasAmiibo();
bool IsEnabled() const;
@@ -37,27 +39,42 @@ private:
struct TagFoundData {
u8 type;
- std::vector<u8> uuid;
+ u8 uuid_size;
+ TagUUID uuid;
};
- DriverResult WaitUntilNfcIsReady();
-
- DriverResult StartPolling(TagFoundData& data, std::size_t timeout_limit = 1);
+ DriverResult WaitUntilNfcIs(NFCStatus status);
- DriverResult ReadTag(const TagFoundData& data);
+ DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
DriverResult GetAmiiboData(std::vector<u8>& data);
- DriverResult SendStartPollingRequest(MCUCommandResponse& output);
+ DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
+
+ DriverResult SendStartPollingRequest(MCUCommandResponse& output,
+ bool is_second_attempt = false);
DriverResult SendStopPollingRequest(MCUCommandResponse& output);
- DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
+ DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id);
DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
+ DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output, const TagUUID& tag_uuid);
+
+ DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
+ bool is_last_packet, std::span<const u8> data);
+
+ std::vector<u8> SerializeWritePackage(const NFCWritePackage& 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;
+
NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
+ TagUUID GetTagUUID(std::span<const u8> data) const;
+
bool is_enabled{};
std::size_t update_counter{};
};
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 91aa96aa7..e4c5b5b3c 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -380,13 +380,16 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
if (!configuring || !mapping_callback.on_data) {
return;
}
+ const auto old_value = GetMotion(identifier, motion);
bool is_active = false;
- if (std::abs(value.accel_x) > 1.5f || std::abs(value.accel_y) > 1.5f ||
- std::abs(value.accel_z) > 1.5f) {
+ if (std::abs(value.accel_x - old_value.accel_x) > 1.5f ||
+ std::abs(value.accel_y - old_value.accel_y) > 1.5f ||
+ std::abs(value.accel_z - old_value.accel_z) > 1.5f) {
is_active = true;
}
- if (std::abs(value.gyro_x) > 0.6f || std::abs(value.gyro_y) > 0.6f ||
- std::abs(value.gyro_z) > 0.6f) {
+ if (std::abs(value.gyro_x - old_value.gyro_x) > 0.6f ||
+ std::abs(value.gyro_y - old_value.gyro_y) > 0.6f ||
+ std::abs(value.gyro_z - old_value.gyro_z) > 0.6f) {
is_active = true;
}
if (!is_active) {