summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hid/emulated_controller.cpp9
-rw-r--r--src/core/hle/service/nfc/common/device.cpp8
-rw-r--r--src/input_common/drivers/joycon.cpp8
-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/joycon_types.h50
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp332
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h27
8 files changed, 387 insertions, 68 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 366880711..bbfea7117 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1283,9 +1283,14 @@ bool EmulatedController::HasNfc() const {
1283} 1283}
1284 1284
1285bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1285bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1286 auto& nfc_output_device = output_devices[3]; 1286 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1287 auto& nfc_virtual_output_device = output_devices[3];
1288
1289 if (nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported) {
1290 return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
1291 }
1287 1292
1288 return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; 1293 return nfc_virtual_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
1289} 1294}
1290 1295
1291void EmulatedController::SetLedPattern() { 1296void EmulatedController::SetLedPattern() {
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index e5d4545a8..0bd7900e1 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -426,11 +426,11 @@ Result NfcDevice::Flush() {
426 426
427 tag_data.write_counter++; 427 tag_data.write_counter++;
428 428
429 FlushWithBreak(NFP::BreakType::Normal); 429 const auto result = FlushWithBreak(NFP::BreakType::Normal);
430 430
431 is_data_moddified = false; 431 is_data_moddified = false;
432 432
433 return ResultSuccess; 433 return result;
434} 434}
435 435
436Result NfcDevice::FlushDebug() { 436Result NfcDevice::FlushDebug() {
@@ -449,11 +449,11 @@ Result NfcDevice::FlushDebug() {
449 449
450 tag_data.write_counter++; 450 tag_data.write_counter++;
451 451
452 FlushWithBreak(NFP::BreakType::Normal); 452 const auto result = FlushWithBreak(NFP::BreakType::Normal);
453 453
454 is_data_moddified = false; 454 is_data_moddified = false;
455 455
456 return ResultSuccess; 456 return result;
457} 457}
458 458
459Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { 459Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 653862a72..b2b5677c8 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -291,9 +291,13 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c
291 return Common::Input::NfcState::Success; 291 return Common::Input::NfcState::Success;
292}; 292};
293 293
294Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_, 294Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
295 const std::vector<u8>& data) { 295 const std::vector<u8>& data) {
296 return Common::Input::NfcState::NotSupported; 296 auto handle = GetHandle(identifier);
297 if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) {
298 return Common::Input::NfcState::WriteFailed;
299 }
300 return Common::Input::NfcState::Success;
297}; 301};
298 302
299Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, 303Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
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/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 353dc744d..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,8 +602,8 @@ 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;
580 INSERT_PADDING_BYTES(0x1); 606 u8 block_id;
581 u8 packet_id; 607 u8 packet_id;
582 MCUPacketFlag packet_flag; 608 MCUPacketFlag packet_flag;
583 u8 data_length; 609 u8 data_length;
@@ -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 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}
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index c9e9af03f..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,18 +39,20 @@ 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 WaitUntilNfcIsPolling();
46 47
47 DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1); 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
@@ -56,8 +60,21 @@ private:
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};