summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_protocol/nfc.cpp
diff options
context:
space:
mode:
authorGravatar liamwhite2023-06-23 09:27:00 -0400
committerGravatar GitHub2023-06-23 09:27:00 -0400
commit87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77 (patch)
tree4239b5df2c866aa26e123719fe838bd6eb0e0394 /src/input_common/helpers/joycon_protocol/nfc.cpp
parentMerge pull request #10884 from liamwhite/spaghetti-vfs (diff)
parentinput_common: Implement native mifare support (diff)
downloadyuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar.gz
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.tar.xz
yuzu-87b9b5d10fa4a30c8eb56f9de44ba0c24d57ae77.zip
Merge pull request #10842 from german77/native_mifare
input_common: Implement native mifare/skylander support for joycons/pro controller
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/nfc.cpp')
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp390
1 files changed, 383 insertions, 7 deletions
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() {
40 if (result == DriverResult::Success) { 40 if (result == DriverResult::Success) {
41 result = WaitUntilNfcIs(NFCStatus::Ready); 41 result = WaitUntilNfcIs(NFCStatus::Ready);
42 } 42 }
43 if (result == DriverResult::Success) {
44 MCUCommandResponse output{};
45 result = SendStopPollingRequest(output);
46 }
47 if (result == DriverResult::Success) {
48 result = WaitUntilNfcIs(NFCStatus::Ready);
49 }
50 if (result == DriverResult::Success) {
51 is_enabled = true;
52 }
43 53
44 return result; 54 return result;
45} 55}
@@ -54,37 +64,50 @@ DriverResult NfcProtocol::DisableNfc() {
54 } 64 }
55 65
56 is_enabled = false; 66 is_enabled = false;
67 is_polling = false;
57 68
58 return result; 69 return result;
59} 70}
60 71
61DriverResult NfcProtocol::StartNFCPollingMode() { 72DriverResult NfcProtocol::StartNFCPollingMode() {
62 LOG_DEBUG(Input, "Start NFC pooling Mode"); 73 LOG_DEBUG(Input, "Start NFC polling Mode");
63 ScopedSetBlocking sb(this); 74 ScopedSetBlocking sb(this);
64 DriverResult result{DriverResult::Success}; 75 DriverResult result{DriverResult::Success};
65 76
66 if (result == DriverResult::Success) { 77 if (result == DriverResult::Success) {
67 MCUCommandResponse output{}; 78 MCUCommandResponse output{};
68 result = SendStopPollingRequest(output); 79 result = SendStartPollingRequest(output);
69 } 80 }
70 if (result == DriverResult::Success) { 81 if (result == DriverResult::Success) {
71 result = WaitUntilNfcIs(NFCStatus::Ready); 82 result = WaitUntilNfcIs(NFCStatus::Polling);
72 } 83 }
73 if (result == DriverResult::Success) { 84 if (result == DriverResult::Success) {
85 is_polling = true;
86 }
87
88 return result;
89}
90
91DriverResult NfcProtocol::StopNFCPollingMode() {
92 LOG_DEBUG(Input, "Stop NFC polling Mode");
93 ScopedSetBlocking sb(this);
94 DriverResult result{DriverResult::Success};
95
96 if (result == DriverResult::Success) {
74 MCUCommandResponse output{}; 97 MCUCommandResponse output{};
75 result = SendStartPollingRequest(output); 98 result = SendStopPollingRequest(output);
76 } 99 }
77 if (result == DriverResult::Success) { 100 if (result == DriverResult::Success) {
78 result = WaitUntilNfcIs(NFCStatus::Polling); 101 result = WaitUntilNfcIs(NFCStatus::WriteReady);
79 } 102 }
80 if (result == DriverResult::Success) { 103 if (result == DriverResult::Success) {
81 is_enabled = true; 104 is_polling = false;
82 } 105 }
83 106
84 return result; 107 return result;
85} 108}
86 109
87DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { 110DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {
88 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 111 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
89 return DriverResult::Delayed; 112 return DriverResult::Delayed;
90 } 113 }
@@ -100,11 +123,41 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
100 } 123 }
101 124
102 if (result == DriverResult::Success) { 125 if (result == DriverResult::Success) {
126 tag_info = {
127 .uuid_length = tag_data.uuid_size,
128 .protocol = 1,
129 .tag_type = tag_data.type,
130 .uuid = {},
131 };
132
133 memcpy(tag_info.uuid.data(), tag_data.uuid.data(), tag_data.uuid_size);
134
135 // Investigate why mifare type is not correct
136 if (tag_info.tag_type == 144) {
137 tag_info.tag_type = 1U << 6;
138 }
139
103 std::string uuid_string; 140 std::string uuid_string;
104 for (auto& content : tag_data.uuid) { 141 for (auto& content : tag_data.uuid) {
105 uuid_string += fmt::format(" {:02x}", content); 142 uuid_string += fmt::format(" {:02x}", content);
106 } 143 }
107 LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string); 144 LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string);
145 }
146
147 return result;
148}
149
150DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) {
151 LOG_DEBUG(Input, "Scan for amiibos");
152 ScopedSetBlocking sb(this);
153 DriverResult result{DriverResult::Success};
154 TagFoundData tag_data{};
155
156 if (result == DriverResult::Success) {
157 result = IsTagInRange(tag_data, 7);
158 }
159
160 if (result == DriverResult::Success) {
108 result = GetAmiiboData(data); 161 result = GetAmiiboData(data);
109 } 162 }
110 163
@@ -154,6 +207,69 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
154 return result; 207 return result;
155} 208}
156 209
210DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request,
211 std::span<MifareReadData> out_data) {
212 LOG_DEBUG(Input, "Read mifare");
213 ScopedSetBlocking sb(this);
214 DriverResult result{DriverResult::Success};
215 TagFoundData tag_data{};
216 MifareUUID tag_uuid{};
217
218 if (result == DriverResult::Success) {
219 result = IsTagInRange(tag_data, 7);
220 }
221 if (result == DriverResult::Success) {
222 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
223 result = GetMifareData(tag_uuid, read_request, out_data);
224 }
225 if (result == DriverResult::Success) {
226 MCUCommandResponse output{};
227 result = SendStopPollingRequest(output);
228 }
229 if (result == DriverResult::Success) {
230 result = WaitUntilNfcIs(NFCStatus::Ready);
231 }
232 if (result == DriverResult::Success) {
233 MCUCommandResponse output{};
234 result = SendStartPollingRequest(output, true);
235 }
236 if (result == DriverResult::Success) {
237 result = WaitUntilNfcIs(NFCStatus::WriteReady);
238 }
239 return result;
240}
241
242DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) {
243 LOG_DEBUG(Input, "Write mifare");
244 ScopedSetBlocking sb(this);
245 DriverResult result{DriverResult::Success};
246 TagFoundData tag_data{};
247 MifareUUID tag_uuid{};
248
249 if (result == DriverResult::Success) {
250 result = IsTagInRange(tag_data, 7);
251 }
252 if (result == DriverResult::Success) {
253 memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
254 result = WriteMifareData(tag_uuid, write_request);
255 }
256 if (result == DriverResult::Success) {
257 MCUCommandResponse output{};
258 result = SendStopPollingRequest(output);
259 }
260 if (result == DriverResult::Success) {
261 result = WaitUntilNfcIs(NFCStatus::Ready);
262 }
263 if (result == DriverResult::Success) {
264 MCUCommandResponse output{};
265 result = SendStartPollingRequest(output, true);
266 }
267 if (result == DriverResult::Success) {
268 result = WaitUntilNfcIs(NFCStatus::WriteReady);
269 }
270 return result;
271}
272
157bool NfcProtocol::HasAmiibo() { 273bool NfcProtocol::HasAmiibo() {
158 if (update_counter++ < AMIIBO_UPDATE_DELAY) { 274 if (update_counter++ < AMIIBO_UPDATE_DELAY) {
159 return true; 275 return true;
@@ -341,6 +457,158 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
341 return result; 457 return result;
342} 458}
343 459
460DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
461 std::span<const MifareReadChunk> read_request,
462 std::span<MifareReadData> out_data) {
463 constexpr std::size_t timeout_limit = 60;
464 const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request);
465 const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data);
466 std::span<const u8> buffer(nfc_buffer_data);
467 DriverResult result = DriverResult::Success;
468 MCUCommandResponse output{};
469 u8 block_id = 1;
470 u8 package_index = 0;
471 std::size_t tries = 0;
472 std::size_t current_position = 0;
473
474 LOG_INFO(Input, "Reading Mifare data");
475
476 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
477 while (current_position < buffer.size() && tries++ < timeout_limit) {
478 const std::size_t next_position =
479 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
480 const std::size_t block_size = next_position - current_position;
481 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
482
483 SendReadDataMifareRequest(output, block_id, is_last_packet,
484 buffer.subspan(current_position, block_size));
485
486 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
487
488 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
489 return DriverResult::ErrorReadingData;
490 }
491
492 // Increase position when data is confirmed by the joycon
493 if (output.mcu_report == MCUReport::NFCState &&
494 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
495 output.mcu_data[3] == block_id) {
496 block_id++;
497 current_position = next_position;
498 }
499 }
500
501 if (result != DriverResult::Success) {
502 return result;
503 }
504
505 // Wait for reply and save the output data
506 while (tries++ < timeout_limit) {
507 result = SendNextPackageRequest(output, package_index);
508 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
509
510 if (result != DriverResult::Success) {
511 return result;
512 }
513
514 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
515 return DriverResult::ErrorReadingData;
516 }
517
518 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
519 constexpr std::size_t DATA_LENGHT = 0x10 + 1;
520 constexpr std::size_t DATA_START = 11;
521 const u8 number_of_elements = output.mcu_data[10];
522 for (std::size_t i = 0; i < number_of_elements; i++) {
523 out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)];
524 memcpy(out_data[i].data.data(),
525 output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT),
526 sizeof(MifareReadData::data));
527 }
528 package_index++;
529 continue;
530 }
531
532 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
533 LOG_INFO(Input, "Finished reading mifare");
534 break;
535 }
536 }
537
538 return result;
539}
540
541DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
542 std::span<const MifareWriteChunk> write_request) {
543 constexpr std::size_t timeout_limit = 60;
544 const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request);
545 const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data);
546 std::span<const u8> buffer(nfc_buffer_data);
547 DriverResult result = DriverResult::Success;
548 MCUCommandResponse output{};
549 u8 block_id = 1;
550 u8 package_index = 0;
551 std::size_t tries = 0;
552 std::size_t current_position = 0;
553
554 LOG_INFO(Input, "Writing Mifare data");
555
556 // Send data request. Nfc buffer size is 31, Send the data in smaller packages
557 while (current_position < buffer.size() && tries++ < timeout_limit) {
558 const std::size_t next_position =
559 std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
560 const std::size_t block_size = next_position - current_position;
561 const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
562
563 SendReadDataMifareRequest(output, block_id, is_last_packet,
564 buffer.subspan(current_position, block_size));
565
566 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
567
568 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
569 return DriverResult::ErrorReadingData;
570 }
571
572 // Increase position when data is confirmed by the joycon
573 if (output.mcu_report == MCUReport::NFCState &&
574 (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
575 output.mcu_data[3] == block_id) {
576 block_id++;
577 current_position = next_position;
578 }
579 }
580
581 if (result != DriverResult::Success) {
582 return result;
583 }
584
585 // Wait for reply and ignore the output data
586 while (tries++ < timeout_limit) {
587 result = SendNextPackageRequest(output, package_index);
588 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
589
590 if (result != DriverResult::Success) {
591 return result;
592 }
593
594 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
595 return DriverResult::ErrorReadingData;
596 }
597
598 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
599 package_index++;
600 continue;
601 }
602
603 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
604 LOG_INFO(Input, "Finished writing mifare");
605 break;
606 }
607 }
608
609 return result;
610}
611
344DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, 612DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
345 bool is_second_attempt) { 613 bool is_second_attempt) {
346 NFCRequestState request{ 614 NFCRequestState request{
@@ -477,6 +745,28 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
477 output); 745 output);
478} 746}
479 747
748DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
749 bool is_last_packet, std::span<const u8> data) {
750 const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
751 NFCRequestState request{
752 .command_argument = NFCCommand::Mifare,
753 .block_id = block_id,
754 .packet_id = {},
755 .packet_flag =
756 is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining,
757 .data_length = static_cast<u8>(data_size),
758 .raw_data = {},
759 .crc = {},
760 };
761 memcpy(request.raw_data.data(), data.data(), data_size);
762
763 std::array<u8, sizeof(NFCRequestState)> request_data{};
764 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
765 request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
766 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
767 output);
768}
769
480std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const { 770std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {
481 const std::size_t header_size = 771 const std::size_t header_size =
482 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); 772 sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks);
@@ -498,6 +788,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag
498 return serialized_data; 788 return serialized_data;
499} 789}
500 790
791std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const {
792 const std::size_t header_size = sizeof(MifareCommandData);
793 std::vector<u8> serialized_data(header_size);
794 std::size_t start_index = 0;
795
796 memcpy(serialized_data.data(), &package, header_size);
797 start_index += header_size;
798
799 for (const auto& data_chunk : package.data_chunks) {
800 const std::size_t chunk_size = sizeof(MifareReadChunk);
801 if (data_chunk.command == MifareCmd::None) {
802 continue;
803 }
804 serialized_data.resize(start_index + chunk_size);
805 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
806 start_index += chunk_size;
807 }
808
809 return serialized_data;
810}
811
812std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const {
813 const std::size_t header_size = sizeof(MifareCommandData);
814 std::vector<u8> serialized_data(header_size);
815 std::size_t start_index = 0;
816
817 memcpy(serialized_data.data(), &package, header_size);
818 start_index += header_size;
819
820 for (const auto& data_chunk : package.data_chunks) {
821 const std::size_t chunk_size = sizeof(MifareWriteChunk);
822 if (data_chunk.command == MifareCmd::None) {
823 continue;
824 }
825 serialized_data.resize(start_index + chunk_size);
826 memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
827 start_index += chunk_size;
828 }
829
830 return serialized_data;
831}
832
501NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, 833NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
502 std::span<const u8> data) const { 834 std::span<const u8> data) const {
503 return { 835 return {
@@ -527,6 +859,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
527 }; 859 };
528} 860}
529 861
862MifareReadPackage NfcProtocol::MakeMifareReadPackage(
863 const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const {
864 MifareReadPackage package{
865 .command_data{
866 .unknown1 = 0xd0,
867 .unknown2 = 0x07,
868 .number_of_short_bytes = static_cast<u8>(
869 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2),
870 .uid = tag_uuid,
871 },
872 .data_chunks = {},
873 };
874
875 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
876 package.data_chunks[i] = read_request[i];
877 }
878
879 return package;
880}
881
882MifareWritePackage NfcProtocol::MakeMifareWritePackage(
883 const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const {
884 MifareWritePackage package{
885 .command_data{
886 .unknown1 = 0xd0,
887 .unknown2 = 0x07,
888 .number_of_short_bytes = static_cast<u8>(
889 ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2),
890 .uid = tag_uuid,
891 },
892 .data_chunks = {},
893 };
894
895 for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
896 package.data_chunks[i] = read_request[i];
897 }
898
899 return package;
900}
901
530NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const { 902NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
531 constexpr u8 NFC_PAGE_SIZE = 4; 903 constexpr u8 NFC_PAGE_SIZE = 4;
532 904
@@ -606,4 +978,8 @@ bool NfcProtocol::IsEnabled() const {
606 return is_enabled; 978 return is_enabled;
607} 979}
608 980
981bool NfcProtocol::IsPolling() const {
982 return is_polling;
983}
984
609} // namespace InputCommon::Joycon 985} // namespace InputCommon::Joycon