summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_protocol/nfc.cpp
diff options
context:
space:
mode:
authorGravatar Narr the Reg2023-05-17 22:17:16 -0600
committerGravatar german772023-05-21 21:09:20 -0600
commitfdb2002f77de6af19cc7f526b2e7540c329161c3 (patch)
tree6bcb2ca55810c05d15a561e2fa0bc0a6c1a9175a /src/input_common/helpers/joycon_protocol/nfc.cpp
parentMerge pull request #10344 from german77/pro-amiibo (diff)
downloadyuzu-fdb2002f77de6af19cc7f526b2e7540c329161c3.tar.gz
yuzu-fdb2002f77de6af19cc7f526b2e7540c329161c3.tar.xz
yuzu-fdb2002f77de6af19cc7f526b2e7540c329161c3.zip
input_common: Implement amiibo writting
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/nfc.cpp')
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp332
1 files changed, 283 insertions, 49 deletions
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index 46c9e9489..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,27 +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);
63 }
64 if (result == DriverResult::Success) {
65 result = WaitUntilNfcIsReady();
66 }
67 if (result == DriverResult::Success) {
68 MCUCommandResponse output{}; 67 MCUCommandResponse output{};
69 result = SendStopPollingRequest(output); 68 result = SendStopPollingRequest(output);
70 } 69 }
71 if (result == DriverResult::Success) { 70 if (result == DriverResult::Success) {
72 result = WaitUntilNfcIsReady(); 71 result = WaitUntilNfcIs(NFCStatus::Ready);
73 } 72 }
74 if (result == DriverResult::Success) { 73 if (result == DriverResult::Success) {
75 MCUCommandResponse output{}; 74 MCUCommandResponse output{};
76 result = SendStartPollingRequest(output); 75 result = SendStartPollingRequest(output);
77 } 76 }
78 if (result == DriverResult::Success) { 77 if (result == DriverResult::Success) {
79 result = WaitUntilNfcIsPolling(); 78 result = WaitUntilNfcIs(NFCStatus::Polling);
80 } 79 }
81 if (result == DriverResult::Success) { 80 if (result == DriverResult::Success) {
82 is_enabled = true; 81 is_enabled = true;
@@ -112,6 +111,49 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
112 return result; 111 return result;
113} 112}
114 113
114DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
115 LOG_DEBUG(Input, "Write amiibo");
116 ScopedSetBlocking sb(this);
117 DriverResult result{DriverResult::Success};
118 TagUUID tag_uuid = GetTagUUID(data);
119 TagFoundData tag_data{};
120
121 if (result == DriverResult::Success) {
122 result = IsTagInRange(tag_data, 7);
123 }
124 if (result == DriverResult::Success) {
125 if (tag_data.uuid != tag_uuid) {
126 result = DriverResult::InvalidParameters;
127 }
128 }
129 if (result == DriverResult::Success) {
130 MCUCommandResponse output{};
131 result = SendStopPollingRequest(output);
132 }
133 if (result == DriverResult::Success) {
134 result = WaitUntilNfcIs(NFCStatus::Ready);
135 }
136 if (result == DriverResult::Success) {
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);
152 }
153
154 return result;
155}
156
115bool NfcProtocol::HasAmiibo() { 157bool NfcProtocol::HasAmiibo() {
116 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 158 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
117 return true; 159 return true;
@@ -129,7 +171,7 @@ bool NfcProtocol::HasAmiibo() {
129 return result == DriverResult::Success; 171 return result == DriverResult::Success;
130} 172}
131 173
132DriverResult NfcProtocol::WaitUntilNfcIsReady() { 174DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
133 constexpr std::size_t timeout_limit = 10; 175 constexpr std::size_t timeout_limit = 10;
134 MCUCommandResponse output{}; 176 MCUCommandResponse output{};
135 std::size_t tries = 0; 177 std::size_t tries = 0;
@@ -145,28 +187,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
145 } 187 }
146 } while (output.mcu_report != MCUReport::NFCState || 188 } while (output.mcu_report != MCUReport::NFCState ||
147 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || 189 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
148 output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00); 190 output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));
149
150 return DriverResult::Success;
151}
152
153DriverResult NfcProtocol::WaitUntilNfcIsPolling() {
154 constexpr std::size_t timeout_limit = 10;
155 MCUCommandResponse output{};
156 std::size_t tries = 0;
157
158 do {
159 auto result = SendNextPackageRequest(output, {});
160
161 if (result != DriverResult::Success) {
162 return result;
163 }
164 if (tries++ > timeout_limit) {
165 return DriverResult::Timeout;
166 }
167 } while (output.mcu_report != MCUReport::NFCState ||
168 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
169 output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x01);
170 191
171 return DriverResult::Success; 192 return DriverResult::Success;
172} 193}
@@ -188,7 +209,7 @@ DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_l
188 (output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04)); 209 (output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04));
189 210
190 data.type = output.mcu_data[12]; 211 data.type = output.mcu_data[12];
191 data.uuid.resize(output.mcu_data[14]); 212 data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID)));
192 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());
193 214
194 return DriverResult::Success; 215 return DriverResult::Success;
@@ -245,17 +266,94 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
245 return DriverResult::Timeout; 266 return DriverResult::Timeout;
246} 267}
247 268
248DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) { 269DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) {
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);
274 MCUCommandResponse output{};
275 u8 block_id = 1;
276 u8 package_index = 0;
277 std::size_t tries = 0;
278 std::size_t current_position = 0;
279
280 LOG_INFO(Input, "Writing amiibo data");
281
282 auto result = SendWriteAmiiboRequest(output, tag_uuid);
283
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);
291 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
292
293 if (result != DriverResult::Success) {
294 return result;
295 }
296
297 if ((output.mcu_report == MCUReport::NFCReadData ||
298 output.mcu_report == MCUReport::NFCState) &&
299 nfc_status == NFCStatus::TagLost) {
300 return DriverResult::ErrorReadingData;
301 }
302
303 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
304 package_index++;
305 continue;
306 }
307
308 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
309 LOG_INFO(Input, "Finished reading amiibo");
310 break;
311 }
312 }
313
314 // Send Data. Nfc buffer size is 31, Send the data in smaller packages
315 while (current_position < buffer.size() && tries++ < timeout_limit) {
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;
330 }
331
332 // Increase position when data is confirmed by the joycon
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;
338 }
339 }
340
341 return result;
342}
343
344DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
345 bool is_second_attempt) {
249 NFCRequestState request{ 346 NFCRequestState request{
250 .command_argument = NFCReadCommand::StartPolling, 347 .command_argument = NFCCommand::StartPolling,
251 .packet_id = 0x0, 348 .block_id = {},
349 .packet_id = {},
252 .packet_flag = MCUPacketFlag::LastCommandPacket, 350 .packet_flag = MCUPacketFlag::LastCommandPacket,
253 .data_length = sizeof(NFCPollingCommandData), 351 .data_length = sizeof(NFCPollingCommandData),
254 .nfc_polling = 352 .nfc_polling =
255 { 353 {
256 .enable_mifare = 0x01, 354 .enable_mifare = 0x00,
257 .unknown_1 = 0x00, 355 .unknown_1 = static_cast<u8>(is_second_attempt ? 0xe8 : 0x00),
258 .unknown_2 = 0x00, 356 .unknown_2 = static_cast<u8>(is_second_attempt ? 0x03 : 0x00),
259 .unknown_3 = 0x2c, 357 .unknown_3 = 0x2c,
260 .unknown_4 = 0x01, 358 .unknown_4 = 0x01,
261 }, 359 },
@@ -271,10 +369,11 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
271 369
272DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { 370DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
273 NFCRequestState request{ 371 NFCRequestState request{
274 .command_argument = NFCReadCommand::StopPolling, 372 .command_argument = NFCCommand::StopPolling,
275 .packet_id = 0x0, 373 .block_id = {},
374 .packet_id = {},
276 .packet_flag = MCUPacketFlag::LastCommandPacket, 375 .packet_flag = MCUPacketFlag::LastCommandPacket,
277 .data_length = 0, 376 .data_length = {},
278 .raw_data = {}, 377 .raw_data = {},
279 .crc = {}, 378 .crc = {},
280 }; 379 };
@@ -288,10 +387,11 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
288 387
289DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) { 388DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {
290 NFCRequestState request{ 389 NFCRequestState request{
291 .command_argument = NFCReadCommand::StartWaitingRecieve, 390 .command_argument = NFCCommand::StartWaitingRecieve,
391 .block_id = {},
292 .packet_id = packet_id, 392 .packet_id = packet_id,
293 .packet_flag = MCUPacketFlag::LastCommandPacket, 393 .packet_flag = MCUPacketFlag::LastCommandPacket,
294 .data_length = 0, 394 .data_length = {},
295 .raw_data = {}, 395 .raw_data = {},
296 .crc = {}, 396 .crc = {},
297 }; 397 };
@@ -305,17 +405,17 @@ DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8
305 405
306DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { 406DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
307 NFCRequestState request{ 407 NFCRequestState request{
308 .command_argument = NFCReadCommand::Ntag, 408 .command_argument = NFCCommand::ReadNtag,
309 .packet_id = 0x0, 409 .block_id = {},
410 .packet_id = {},
310 .packet_flag = MCUPacketFlag::LastCommandPacket, 411 .packet_flag = MCUPacketFlag::LastCommandPacket,
311 .data_length = sizeof(NFCReadCommandData), 412 .data_length = sizeof(NFCReadCommandData),
312 .nfc_read = 413 .nfc_read =
313 { 414 {
314 .unknown = 0xd0, 415 .unknown = 0xd0,
315 .uuid_length = 0x07, 416 .uuid_length = sizeof(NFCReadCommandData::uid),
316 .unknown_2 = 0x00,
317 .uid = {}, 417 .uid = {},
318 .tag_type = NFCTagType::AllTags, 418 .tag_type = NFCTagType::Ntag215,
319 .read_block = GetReadBlockCommand(ntag_pages), 419 .read_block = GetReadBlockCommand(ntag_pages),
320 }, 420 },
321 .crc = {}, 421 .crc = {},
@@ -328,12 +428,135 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
328 output); 428 output);
329} 429}
330 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
331NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { 546NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
332 switch (pages) { 547 switch (pages) {
333 case NFCPages::Block0: 548 case NFCPages::Block0:
334 return { 549 return {
335 .block_count = 1, 550 .block_count = 1,
336 }; 551 };
552 case NFCPages::Block3:
553 return {
554 .block_count = 1,
555 .blocks =
556 {
557 NFCReadBlock{0x03, 0x03},
558 },
559 };
337 case NFCPages::Block45: 560 case NFCPages::Block45:
338 return { 561 return {
339 .block_count = 1, 562 .block_count = 1,
@@ -368,6 +591,17 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
368 }; 591 };
369} 592}
370 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
371bool NfcProtocol::IsEnabled() const { 605bool NfcProtocol::IsEnabled() const {
372 return is_enabled; 606 return is_enabled;
373} 607}