summaryrefslogtreecommitdiff
path: root/src/input_common/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/helpers')
-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
6 files changed, 388 insertions, 113 deletions
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() {
492 return result; 492 return result;
493} 493}
494 494
495DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
496 std::scoped_lock lock{mutex};
497 disable_input_thread = true;
498
499 if (!supported_features.nfc) {
500 return DriverResult::NotSupported;
501 }
502 if (!nfc_protocol->IsEnabled()) {
503 return DriverResult::Disabled;
504 }
505 if (!amiibo_detected) {
506 return DriverResult::ErrorWritingData;
507 }
508
509 const auto result = nfc_protocol->WriteAmiibo(data);
510
511 disable_input_thread = false;
512 return result;
513}
514
495bool JoyconDriver::IsConnected() const { 515bool JoyconDriver::IsConnected() const {
496 std::scoped_lock lock{mutex}; 516 std::scoped_lock lock{mutex};
497 return is_connected.load(); 517 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:
49 DriverResult SetIrMode(); 49 DriverResult SetIrMode();
50 DriverResult SetNfcMode(); 50 DriverResult SetNfcMode();
51 DriverResult SetRingConMode(); 51 DriverResult SetRingConMode();
52 DriverResult WriteNfcData(std::span<const u8> data);
52 53
53 void SetCallbacks(const JoyconCallbacks& callbacks); 54 void SetCallbacks(const JoyconCallbacks& callbacks);
54 55
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
265 265
266DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { 266DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
267 MCUCommandResponse output{}; 267 MCUCommandResponse output{};
268 constexpr std::size_t MaxTries{8}; 268 constexpr std::size_t MaxTries{16};
269 std::size_t tries{}; 269 std::size_t tries{};
270 270
271 do { 271 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
23 23
24using MacAddress = std::array<u8, 6>; 24using MacAddress = std::array<u8, 6>;
25using SerialNumber = std::array<u8, 15>; 25using SerialNumber = std::array<u8, 15>;
26using TagUUID = std::array<u8, 7>;
26 27
27enum class ControllerType : u8 { 28enum class ControllerType : u8 {
28 None = 0x00, 29 None = 0x00,
@@ -276,12 +277,13 @@ enum class MCUPacketFlag : u8 {
276 LastCommandPacket = 0x08, 277 LastCommandPacket = 0x08,
277}; 278};
278 279
279enum class NFCReadCommand : u8 { 280enum class NFCCommand : u8 {
280 CancelAll = 0x00, 281 CancelAll = 0x00,
281 StartPolling = 0x01, 282 StartPolling = 0x01,
282 StopPolling = 0x02, 283 StopPolling = 0x02,
283 StartWaitingRecieve = 0x04, 284 StartWaitingRecieve = 0x04,
284 Ntag = 0x06, 285 ReadNtag = 0x06,
286 WriteNtag = 0x08,
285 Mifare = 0x0F, 287 Mifare = 0x0F,
286}; 288};
287 289
@@ -292,14 +294,19 @@ enum class NFCTagType : u8 {
292 294
293enum class NFCPages { 295enum class NFCPages {
294 Block0 = 0, 296 Block0 = 0,
297 Block3 = 3,
295 Block45 = 45, 298 Block45 = 45,
296 Block135 = 135, 299 Block135 = 135,
297 Block231 = 231, 300 Block231 = 231,
298}; 301};
299 302
300enum class NFCStatus : u8 { 303enum class NFCStatus : u8 {
304 Ready = 0x00,
305 Polling = 0x01,
301 LastPackage = 0x04, 306 LastPackage = 0x04,
307 WriteDone = 0x05,
302 TagLost = 0x07, 308 TagLost = 0x07,
309 WriteReady = 0x09,
303}; 310};
304 311
305enum class IrsMode : u8 { 312enum class IrsMode : u8 {
@@ -559,13 +566,32 @@ static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an inv
559struct NFCReadCommandData { 566struct NFCReadCommandData {
560 u8 unknown; 567 u8 unknown;
561 u8 uuid_length; 568 u8 uuid_length;
562 u8 unknown_2; 569 TagUUID uid;
563 std::array<u8, 6> uid;
564 NFCTagType tag_type; 570 NFCTagType tag_type;
565 NFCReadBlockCommand read_block; 571 NFCReadBlockCommand read_block;
566}; 572};
567static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size"); 573static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
568 574
575#pragma pack(push, 1)
576struct NFCWriteCommandData {
577 u8 unknown;
578 u8 uuid_length;
579 TagUUID uid;
580 NFCTagType tag_type;
581 u8 unknown2;
582 u8 unknown3;
583 u8 unknown4;
584 u8 unknown5;
585 u8 unknown6;
586 u8 unknown7;
587 u8 unknown8;
588 u8 magic;
589 u16_be write_count;
590 u8 amiibo_version;
591};
592static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
593#pragma pack(pop)
594
569struct NFCPollingCommandData { 595struct NFCPollingCommandData {
570 u8 enable_mifare; 596 u8 enable_mifare;
571 u8 unknown_1; 597 u8 unknown_1;
@@ -576,9 +602,9 @@ struct NFCPollingCommandData {
576static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size"); 602static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
577 603
578struct NFCRequestState { 604struct NFCRequestState {
579 NFCReadCommand command_argument; 605 NFCCommand command_argument;
606 u8 block_id;
580 u8 packet_id; 607 u8 packet_id;
581 INSERT_PADDING_BYTES(0x1);
582 MCUPacketFlag packet_flag; 608 MCUPacketFlag packet_flag;
583 u8 data_length; 609 u8 data_length;
584 union { 610 union {
@@ -591,6 +617,18 @@ struct NFCRequestState {
591}; 617};
592static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); 618static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
593 619
620struct NFCDataChunk {
621 u8 nfc_page;
622 u8 data_size;
623 std::array<u8, 0xFF> data;
624};
625
626struct NFCWritePackage {
627 NFCWriteCommandData command_data;
628 u8 number_of_chunks;
629 std::array<NFCDataChunk, 4> data_chunks;
630};
631
594struct IrsConfigure { 632struct IrsConfigure {
595 MCUCommand command; 633 MCUCommand command;
596 MCUSubCommand sub_command; 634 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() {
34 34
35 result = ConfigureMCU(config); 35 result = ConfigureMCU(config);
36 } 36 }
37 if (result == DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
39 }
40 if (result == DriverResult::Success) {
41 result = WaitUntilNfcIs(NFCStatus::Ready);
42 }
37 43
38 return result; 44 return result;
39} 45}
@@ -56,13 +62,20 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
56 LOG_DEBUG(Input, "Start NFC pooling Mode"); 62 LOG_DEBUG(Input, "Start NFC pooling Mode");
57 ScopedSetBlocking sb(this); 63 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success}; 64 DriverResult result{DriverResult::Success};
59 TagFoundData tag_data{};
60 65
61 if (result == DriverResult::Success) { 66 if (result == DriverResult::Success) {
62 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); 67 MCUCommandResponse output{};
68 result = SendStopPollingRequest(output);
69 }
70 if (result == DriverResult::Success) {
71 result = WaitUntilNfcIs(NFCStatus::Ready);
72 }
73 if (result == DriverResult::Success) {
74 MCUCommandResponse output{};
75 result = SendStartPollingRequest(output);
63 } 76 }
64 if (result == DriverResult::Success) { 77 if (result == DriverResult::Success) {
65 result = WaitUntilNfcIsReady(); 78 result = WaitUntilNfcIs(NFCStatus::Polling);
66 } 79 }
67 if (result == DriverResult::Success) { 80 if (result == DriverResult::Success) {
68 is_enabled = true; 81 is_enabled = true;
@@ -77,49 +90,94 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
77 } 90 }
78 update_counter = 0; 91 update_counter = 0;
79 92
80 LOG_DEBUG(Input, "Start NFC pooling Mode"); 93 LOG_DEBUG(Input, "Scan for amiibos");
94 ScopedSetBlocking sb(this);
95 DriverResult result{DriverResult::Success};
96 TagFoundData tag_data{};
97
98 if (result == DriverResult::Success) {
99 result = IsTagInRange(tag_data);
100 }
101
102 if (result == DriverResult::Success) {
103 std::string uuid_string;
104 for (auto& content : tag_data.uuid) {
105 uuid_string += fmt::format(" {:02x}", content);
106 }
107 LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string);
108 result = GetAmiiboData(data);
109 }
110
111 return result;
112}
113
114DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
115 LOG_DEBUG(Input, "Write amiibo");
81 ScopedSetBlocking sb(this); 116 ScopedSetBlocking sb(this);
82 DriverResult result{DriverResult::Success}; 117 DriverResult result{DriverResult::Success};
118 TagUUID tag_uuid = GetTagUUID(data);
83 TagFoundData tag_data{}; 119 TagFoundData tag_data{};
84 120
85 if (result == DriverResult::Success) { 121 if (result == DriverResult::Success) {
86 result = StartPolling(tag_data); 122 result = IsTagInRange(tag_data, 7);
87 } 123 }
88 if (result == DriverResult::Success) { 124 if (result == DriverResult::Success) {
89 result = ReadTag(tag_data); 125 if (tag_data.uuid != tag_uuid) {
126 result = DriverResult::InvalidParameters;
127 }
90 } 128 }
91 if (result == DriverResult::Success) { 129 if (result == DriverResult::Success) {
92 result = WaitUntilNfcIsReady(); 130 MCUCommandResponse output{};
131 result = SendStopPollingRequest(output);
93 } 132 }
94 if (result == DriverResult::Success) { 133 if (result == DriverResult::Success) {
95 result = StartPolling(tag_data, 7); 134 result = WaitUntilNfcIs(NFCStatus::Ready);
96 } 135 }
97 if (result == DriverResult::Success) { 136 if (result == DriverResult::Success) {
98 result = GetAmiiboData(data); 137 MCUCommandResponse output{};
138 result = SendStartPollingRequest(output, true);
139 }
140 if (result == DriverResult::Success) {
141 result = WaitUntilNfcIs(NFCStatus::WriteReady);
142 }
143 if (result == DriverResult::Success) {
144 result = WriteAmiiboData(tag_uuid, data);
145 }
146 if (result == DriverResult::Success) {
147 result = WaitUntilNfcIs(NFCStatus::WriteDone);
148 }
149 if (result == DriverResult::Success) {
150 MCUCommandResponse output{};
151 result = SendStopPollingRequest(output);
99 } 152 }
100 153
101 return result; 154 return result;
102} 155}
103 156
104bool NfcProtocol::HasAmiibo() { 157bool NfcProtocol::HasAmiibo() {
158 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
159 return true;
160 }
161 update_counter = 0;
162
105 ScopedSetBlocking sb(this); 163 ScopedSetBlocking sb(this);
106 DriverResult result{DriverResult::Success}; 164 DriverResult result{DriverResult::Success};
107 TagFoundData tag_data{}; 165 TagFoundData tag_data{};
108 166
109 if (result == DriverResult::Success) { 167 if (result == DriverResult::Success) {
110 result = StartPolling(tag_data); 168 result = IsTagInRange(tag_data, 7);
111 } 169 }
112 170
113 return result == DriverResult::Success; 171 return result == DriverResult::Success;
114} 172}
115 173
116DriverResult NfcProtocol::WaitUntilNfcIsReady() { 174DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
117 constexpr std::size_t timeout_limit = 10; 175 constexpr std::size_t timeout_limit = 10;
118 MCUCommandResponse output{}; 176 MCUCommandResponse output{};
119 std::size_t tries = 0; 177 std::size_t tries = 0;
120 178
121 do { 179 do {
122 auto result = SendStartWaitingRecieveRequest(output); 180 auto result = SendNextPackageRequest(output, {});
123 181
124 if (result != DriverResult::Success) { 182 if (result != DriverResult::Success) {
125 return result; 183 return result;
@@ -129,18 +187,17 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
129 } 187 }
130 } while (output.mcu_report != MCUReport::NFCState || 188 } while (output.mcu_report != MCUReport::NFCState ||
131 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || 189 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
132 output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00); 190 output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));
133 191
134 return DriverResult::Success; 192 return DriverResult::Success;
135} 193}
136 194
137DriverResult NfcProtocol::StartPolling(TagFoundData& data, std::size_t timeout_limit) { 195DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_limit) {
138 LOG_DEBUG(Input, "Start Polling for tag");
139 MCUCommandResponse output{}; 196 MCUCommandResponse output{};
140 std::size_t tries = 0; 197 std::size_t tries = 0;
141 198
142 do { 199 do {
143 const auto result = SendStartPollingRequest(output); 200 const auto result = SendNextPackageRequest(output, {});
144 if (result != DriverResult::Success) { 201 if (result != DriverResult::Success) {
145 return result; 202 return result;
146 } 203 }
@@ -149,32 +206,31 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data, std::size_t timeout_l
149 } 206 }
150 } while (output.mcu_report != MCUReport::NFCState || 207 } while (output.mcu_report != MCUReport::NFCState ||
151 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || 208 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
152 output.mcu_data[6] != 0x09); 209 (output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04));
153 210
154 data.type = output.mcu_data[12]; 211 data.type = output.mcu_data[12];
155 data.uuid.resize(output.mcu_data[14]); 212 data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID)));
156 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size()); 213 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
157 214
158 return DriverResult::Success; 215 return DriverResult::Success;
159} 216}
160 217
161DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { 218DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
162 constexpr std::size_t timeout_limit = 10; 219 constexpr std::size_t timeout_limit = 60;
163 MCUCommandResponse output{}; 220 MCUCommandResponse output{};
164 std::size_t tries = 0; 221 std::size_t tries = 0;
165 222
166 std::string uuid_string; 223 u8 package_index = 0;
167 for (auto& content : data.uuid) { 224 std::size_t ntag_buffer_pos = 0;
168 uuid_string += fmt::format(" {:02x}", content); 225 auto result = SendReadAmiiboRequest(output, NFCPages::Block135);
169 }
170 226
171 LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string); 227 if (result != DriverResult::Success) {
228 return result;
229 }
172 230
173 tries = 0;
174 NFCPages ntag_pages = NFCPages::Block0;
175 // Read Tag data 231 // Read Tag data
176 while (true) { 232 while (tries++ < timeout_limit) {
177 auto result = SendReadAmiiboRequest(output, ntag_pages); 233 result = SendNextPackageRequest(output, package_index);
178 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 234 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
179 235
180 if (result != DriverResult::Success) { 236 if (result != DriverResult::Success) {
@@ -187,56 +243,51 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
187 return DriverResult::ErrorReadingData; 243 return DriverResult::ErrorReadingData;
188 } 244 }
189 245
190 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 && 246 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
191 output.mcu_data[2] == 0x01) { 247 std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
192 if (data.type != 2) { 248 if (output.mcu_data[2] == 0x01) {
193 continue; 249 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
194 } 250 payload_size - 60);
195 switch (output.mcu_data[24]) { 251 ntag_buffer_pos += payload_size - 60;
196 case 0: 252 } else {
197 ntag_pages = NFCPages::Block135; 253 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
198 break; 254 payload_size);
199 case 3:
200 ntag_pages = NFCPages::Block45;
201 break;
202 case 4:
203 ntag_pages = NFCPages::Block231;
204 break;
205 default:
206 return DriverResult::ErrorReadingData;
207 } 255 }
256 package_index++;
208 continue; 257 continue;
209 } 258 }
210 259
211 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 260 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
212 // finished 261 LOG_INFO(Input, "Finished reading amiibo");
213 SendStopPollingRequest(output);
214 return DriverResult::Success; 262 return DriverResult::Success;
215 } 263 }
216
217 // Ignore other state reports
218 if (output.mcu_report == MCUReport::NFCState) {
219 continue;
220 }
221
222 if (tries++ > timeout_limit) {
223 return DriverResult::Timeout;
224 }
225 } 264 }
226 265
227 return DriverResult::Success; 266 return DriverResult::Timeout;
228} 267}
229 268
230DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { 269DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) {
231 constexpr std::size_t timeout_limit = 10; 270 constexpr std::size_t timeout_limit = 60;
271 const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data);
272 const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data);
273 std::span<const u8> buffer(nfc_buffer_data);
232 MCUCommandResponse output{}; 274 MCUCommandResponse output{};
275 u8 block_id = 1;
276 u8 package_index = 0;
233 std::size_t tries = 0; 277 std::size_t tries = 0;
278 std::size_t current_position = 0;
234 279
235 NFCPages ntag_pages = NFCPages::Block135; 280 LOG_INFO(Input, "Writing amiibo data");
236 std::size_t ntag_buffer_pos = 0; 281
237 // Read Tag data 282 auto result = SendWriteAmiiboRequest(output, tag_uuid);
238 while (true) { 283
239 auto result = SendReadAmiiboRequest(output, ntag_pages); 284 if (result != DriverResult::Success) {
285 return result;
286 }
287
288 // Read Tag data but ignore the actual sent data
289 while (tries++ < timeout_limit) {
290 result = SendNextPackageRequest(output, package_index);
240 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); 291 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
241 292
242 if (result != DriverResult::Success) { 293 if (result != DriverResult::Success) {
@@ -250,47 +301,59 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
250 } 301 }
251 302
252 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { 303 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
253 std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF; 304 package_index++;
254 if (output.mcu_data[2] == 0x01) {
255 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
256 payload_size - 60);
257 ntag_buffer_pos += payload_size - 60;
258 } else {
259 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
260 payload_size);
261 }
262 continue; 305 continue;
263 } 306 }
264 307
265 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 308 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
266 LOG_INFO(Input, "Finished reading amiibo"); 309 LOG_INFO(Input, "Finished reading amiibo");
267 return DriverResult::Success; 310 break;
268 } 311 }
312 }
269 313
270 // Ignore other state reports 314 // Send Data. Nfc buffer size is 31, Send the data in smaller packages
271 if (output.mcu_report == MCUReport::NFCState) { 315 while (current_position < buffer.size() && tries++ < timeout_limit) {
272 continue; 316 const std::size_t next_position =
317 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
318 const std::size_t block_size = next_position - current_position;
319 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
320
321 SendWriteDataAmiiboRequest(output, block_id, is_last_packet,
322 buffer.subspan(current_position, block_size));
323
324 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
325
326 if ((output.mcu_report == MCUReport::NFCReadData ||
327 output.mcu_report == MCUReport::NFCState) &&
328 nfc_status == NFCStatus::TagLost) {
329 return DriverResult::ErrorReadingData;
273 } 330 }
274 331
275 if (tries++ > timeout_limit) { 332 // Increase position when data is confirmed by the joycon
276 return DriverResult::Timeout; 333 if (output.mcu_report == MCUReport::NFCState &&
334 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
335 output.mcu_data[3] == block_id) {
336 block_id++;
337 current_position = next_position;
277 } 338 }
278 } 339 }
279 340
280 return DriverResult::Success; 341 return result;
281} 342}
282 343
283DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) { 344DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
345 bool is_second_attempt) {
284 NFCRequestState request{ 346 NFCRequestState request{
285 .command_argument = NFCReadCommand::StartPolling, 347 .command_argument = NFCCommand::StartPolling,
286 .packet_id = 0x0, 348 .block_id = {},
349 .packet_id = {},
287 .packet_flag = MCUPacketFlag::LastCommandPacket, 350 .packet_flag = MCUPacketFlag::LastCommandPacket,
288 .data_length = sizeof(NFCPollingCommandData), 351 .data_length = sizeof(NFCPollingCommandData),
289 .nfc_polling = 352 .nfc_polling =
290 { 353 {
291 .enable_mifare = 0x01, 354 .enable_mifare = 0x00,
292 .unknown_1 = 0x00, 355 .unknown_1 = static_cast<u8>(is_second_attempt ? 0xe8 : 0x00),
293 .unknown_2 = 0x00, 356 .unknown_2 = static_cast<u8>(is_second_attempt ? 0x03 : 0x00),
294 .unknown_3 = 0x2c, 357 .unknown_3 = 0x2c,
295 .unknown_4 = 0x01, 358 .unknown_4 = 0x01,
296 }, 359 },
@@ -306,10 +369,11 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
306 369
307DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { 370DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
308 NFCRequestState request{ 371 NFCRequestState request{
309 .command_argument = NFCReadCommand::StopPolling, 372 .command_argument = NFCCommand::StopPolling,
310 .packet_id = 0x0, 373 .block_id = {},
374 .packet_id = {},
311 .packet_flag = MCUPacketFlag::LastCommandPacket, 375 .packet_flag = MCUPacketFlag::LastCommandPacket,
312 .data_length = 0, 376 .data_length = {},
313 .raw_data = {}, 377 .raw_data = {},
314 .crc = {}, 378 .crc = {},
315 }; 379 };
@@ -321,12 +385,13 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
321 output); 385 output);
322} 386}
323 387
324DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) { 388DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {
325 NFCRequestState request{ 389 NFCRequestState request{
326 .command_argument = NFCReadCommand::StartWaitingRecieve, 390 .command_argument = NFCCommand::StartWaitingRecieve,
327 .packet_id = 0x0, 391 .block_id = {},
392 .packet_id = packet_id,
328 .packet_flag = MCUPacketFlag::LastCommandPacket, 393 .packet_flag = MCUPacketFlag::LastCommandPacket,
329 .data_length = 0, 394 .data_length = {},
330 .raw_data = {}, 395 .raw_data = {},
331 .crc = {}, 396 .crc = {},
332 }; 397 };
@@ -340,17 +405,17 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& out
340 405
341DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { 406DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
342 NFCRequestState request{ 407 NFCRequestState request{
343 .command_argument = NFCReadCommand::Ntag, 408 .command_argument = NFCCommand::ReadNtag,
344 .packet_id = 0x0, 409 .block_id = {},
410 .packet_id = {},
345 .packet_flag = MCUPacketFlag::LastCommandPacket, 411 .packet_flag = MCUPacketFlag::LastCommandPacket,
346 .data_length = sizeof(NFCReadCommandData), 412 .data_length = sizeof(NFCReadCommandData),
347 .nfc_read = 413 .nfc_read =
348 { 414 {
349 .unknown = 0xd0, 415 .unknown = 0xd0,
350 .uuid_length = 0x07, 416 .uuid_length = sizeof(NFCReadCommandData::uid),
351 .unknown_2 = 0x00,
352 .uid = {}, 417 .uid = {},
353 .tag_type = NFCTagType::AllTags, 418 .tag_type = NFCTagType::Ntag215,
354 .read_block = GetReadBlockCommand(ntag_pages), 419 .read_block = GetReadBlockCommand(ntag_pages),
355 }, 420 },
356 .crc = {}, 421 .crc = {},
@@ -363,12 +428,135 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
363 output); 428 output);
364} 429}
365 430
431DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output,
432 const TagUUID& tag_uuid) {
433 NFCRequestState request{
434 .command_argument = NFCCommand::ReadNtag,
435 .block_id = {},
436 .packet_id = {},
437 .packet_flag = MCUPacketFlag::LastCommandPacket,
438 .data_length = sizeof(NFCReadCommandData),
439 .nfc_read =
440 {
441 .unknown = 0xd0,
442 .uuid_length = sizeof(NFCReadCommandData::uid),
443 .uid = tag_uuid,
444 .tag_type = NFCTagType::Ntag215,
445 .read_block = GetReadBlockCommand(NFCPages::Block3),
446 },
447 .crc = {},
448 };
449
450 std::array<u8, sizeof(NFCRequestState)> request_data{};
451 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
452 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
453 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
454 output);
455}
456
457DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
458 bool is_last_packet,
459 std::span<const u8> data) {
460 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
461 NFCRequestState request{
462 .command_argument = NFCCommand::WriteNtag,
463 .block_id = block_id,
464 .packet_id = {},
465 .packet_flag =
466 is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining,
467 .data_length = static_cast<u8>(data_size),
468 .raw_data = {},
469 .crc = {},
470 };
471 memcpy(request.raw_data.data(), data.data(), data_size);
472
473 std::array<u8, sizeof(NFCRequestState)> request_data{};
474 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
475 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
476 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
477 output);
478}
479
480std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {
481 const std::size_t header_size =
482 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks);
483 std::vector<u8> serialized_data(header_size);
484 std::size_t start_index = 0;
485
486 memcpy(serialized_data.data(), &package, header_size);
487 start_index += header_size;
488
489 for (const auto& data_chunk : package.data_chunks) {
490 const std::size_t chunk_size =
491 sizeof(NFCDataChunk::nfc_page) + sizeof(NFCDataChunk::data_size) + data_chunk.data_size;
492
493 serialized_data.resize(start_index + chunk_size);
494 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
495 start_index += chunk_size;
496 }
497
498 return serialized_data;
499}
500
501NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
502 std::span<const u8> data) const {
503 return {
504 .command_data{
505 .unknown = 0xd0,
506 .uuid_length = sizeof(NFCReadCommandData::uid),
507 .uid = tag_uuid,
508 .tag_type = NFCTagType::Ntag215,
509 .unknown2 = 0x00,
510 .unknown3 = 0x01,
511 .unknown4 = 0x04,
512 .unknown5 = 0xff,
513 .unknown6 = 0xff,
514 .unknown7 = 0xff,
515 .unknown8 = 0xff,
516 .magic = data[16],
517 .write_count = static_cast<u16>((data[17] << 8) + data[18]),
518 .amiibo_version = data[19],
519 },
520 .number_of_chunks = 3,
521 .data_chunks =
522 {
523 MakeAmiiboChunk(0x05, 0x20, data),
524 MakeAmiiboChunk(0x20, 0xf0, data),
525 MakeAmiiboChunk(0x5c, 0x98, data),
526 },
527 };
528}
529
530NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
531 constexpr u8 PAGE_SIZE = 4;
532
533 if (static_cast<std::size_t>(page * PAGE_SIZE) + size >= data.size()) {
534 return {};
535 }
536
537 NFCDataChunk chunk{
538 .nfc_page = page,
539 .data_size = size,
540 .data = {},
541 };
542 std::memcpy(chunk.data.data(), data.data() + (page * PAGE_SIZE), size);
543 return chunk;
544}
545
366NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { 546NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
367 switch (pages) { 547 switch (pages) {
368 case NFCPages::Block0: 548 case NFCPages::Block0:
369 return { 549 return {
370 .block_count = 1, 550 .block_count = 1,
371 }; 551 };
552 case NFCPages::Block3:
553 return {
554 .block_count = 1,
555 .blocks =
556 {
557 NFCReadBlock{0x03, 0x03},
558 },
559 };
372 case NFCPages::Block45: 560 case NFCPages::Block45:
373 return { 561 return {
374 .block_count = 1, 562 .block_count = 1,
@@ -403,6 +591,17 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
403 }; 591 };
404} 592}
405 593
594TagUUID NfcProtocol::GetTagUUID(std::span<const u8> data) const {
595 if (data.size() < 10) {
596 return {};
597 }
598
599 // crc byte 3 is omitted in this operation
600 return {
601 data[0], data[1], data[2], data[4], data[5], data[6], data[7],
602 };
603}
604
406bool NfcProtocol::IsEnabled() const { 605bool NfcProtocol::IsEnabled() const {
407 return is_enabled; 606 return is_enabled;
408} 607}
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:
27 27
28 DriverResult ScanAmiibo(std::vector<u8>& data); 28 DriverResult ScanAmiibo(std::vector<u8>& data);
29 29
30 DriverResult WriteAmiibo(std::span<const u8> data);
31
30 bool HasAmiibo(); 32 bool HasAmiibo();
31 33
32 bool IsEnabled() const; 34 bool IsEnabled() const;
@@ -37,27 +39,42 @@ private:
37 39
38 struct TagFoundData { 40 struct TagFoundData {
39 u8 type; 41 u8 type;
40 std::vector<u8> uuid; 42 u8 uuid_size;
43 TagUUID uuid;
41 }; 44 };
42 45
43 DriverResult WaitUntilNfcIsReady(); 46 DriverResult WaitUntilNfcIs(NFCStatus status);
44
45 DriverResult StartPolling(TagFoundData& data, std::size_t timeout_limit = 1);
46 47
47 DriverResult ReadTag(const TagFoundData& data); 48 DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
48 49
49 DriverResult GetAmiiboData(std::vector<u8>& data); 50 DriverResult GetAmiiboData(std::vector<u8>& data);
50 51
51 DriverResult SendStartPollingRequest(MCUCommandResponse& output); 52 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
53
54 DriverResult SendStartPollingRequest(MCUCommandResponse& output,
55 bool is_second_attempt = false);
52 56
53 DriverResult SendStopPollingRequest(MCUCommandResponse& output); 57 DriverResult SendStopPollingRequest(MCUCommandResponse& output);
54 58
55 DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output); 59 DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id);
56 60
57 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages); 61 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
58 62
63 DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output, const TagUUID& tag_uuid);
64
65 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
66 bool is_last_packet, std::span<const u8> data);
67
68 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
69
70 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;
71
72 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const;
73
59 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; 74 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
60 75
76 TagUUID GetTagUUID(std::span<const u8> data) const;
77
61 bool is_enabled{}; 78 bool is_enabled{};
62 std::size_t update_counter{}; 79 std::size_t update_counter{};
63}; 80};