summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Narr the Reg2023-06-16 21:57:21 -0600
committerGravatar Narr the Reg2023-06-21 17:54:58 -0600
commit84d43489c5df9f450efb0293cc58161d08e3b882 (patch)
treec4d45b021c78392956dc58d409a34632fe135d2b
parentMerge pull request #10783 from liamwhite/memory (diff)
downloadyuzu-84d43489c5df9f450efb0293cc58161d08e3b882.tar.gz
yuzu-84d43489c5df9f450efb0293cc58161d08e3b882.tar.xz
yuzu-84d43489c5df9f450efb0293cc58161d08e3b882.zip
input_common: Implement native mifare support
-rw-r--r--src/common/input.h43
-rw-r--r--src/core/hid/emulated_controller.cpp82
-rw-r--r--src/core/hid/emulated_controller.h28
-rw-r--r--src/core/hid/input_converter.cpp6
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp2
-rw-r--r--src/core/hle/service/nfc/common/device.cpp182
-rw-r--r--src/core/hle/service/nfc/common/device.h10
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp11
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h2
-rw-r--r--src/core/hle/service/nfc/mifare_types.h11
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp7
-rw-r--r--src/input_common/drivers/joycon.cpp133
-rw-r--r--src/input_common/drivers/joycon.h18
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp130
-rw-r--r--src/input_common/drivers/virtual_amiibo.h16
-rw-r--r--src/input_common/helpers/joycon_driver.cpp118
-rw-r--r--src/input_common/helpers/joycon_driver.h6
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h59
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp390
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h34
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp4
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.h4
-rw-r--r--src/input_common/input_engine.h34
-rw-r--r--src/input_common/input_poller.cpp24
-rw-r--r--src/yuzu/main.cpp4
25 files changed, 1165 insertions, 193 deletions
diff --git a/src/common/input.h b/src/common/input.h
index 66fb15f0a..ea30770ae 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -86,7 +86,7 @@ enum class NfcState {
86 NewAmiibo, 86 NewAmiibo,
87 WaitingForAmiibo, 87 WaitingForAmiibo,
88 AmiiboRemoved, 88 AmiiboRemoved,
89 NotAnAmiibo, 89 InvalidTagType,
90 NotSupported, 90 NotSupported,
91 WrongDeviceState, 91 WrongDeviceState,
92 WriteFailed, 92 WriteFailed,
@@ -218,8 +218,22 @@ struct CameraStatus {
218}; 218};
219 219
220struct NfcStatus { 220struct NfcStatus {
221 NfcState state{}; 221 NfcState state{NfcState::Unknown};
222 std::vector<u8> data{}; 222 u8 uuid_length;
223 u8 protocol;
224 u8 tag_type;
225 std::array<u8, 10> uuid;
226};
227
228struct MifareData {
229 u8 command;
230 u8 sector;
231 std::array<u8, 0x6> key;
232 std::array<u8, 0x10> data;
233};
234
235struct MifareRequest {
236 std::array<MifareData, 0x10> data;
223}; 237};
224 238
225// List of buttons to be passed to Qt that can be translated 239// List of buttons to be passed to Qt that can be translated
@@ -294,7 +308,7 @@ struct CallbackStatus {
294 BatteryStatus battery_status{}; 308 BatteryStatus battery_status{};
295 VibrationStatus vibration_status{}; 309 VibrationStatus vibration_status{};
296 CameraFormat camera_status{CameraFormat::None}; 310 CameraFormat camera_status{CameraFormat::None};
297 NfcState nfc_status{NfcState::Unknown}; 311 NfcStatus nfc_status{};
298 std::vector<u8> raw_data{}; 312 std::vector<u8> raw_data{};
299}; 313};
300 314
@@ -356,9 +370,30 @@ public:
356 return NfcState::NotSupported; 370 return NfcState::NotSupported;
357 } 371 }
358 372
373 virtual NfcState StartNfcPolling() {
374 return NfcState::NotSupported;
375 }
376
377 virtual NfcState StopNfcPolling() {
378 return NfcState::NotSupported;
379 }
380
381 virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) {
382 return NfcState::NotSupported;
383 }
384
359 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { 385 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
360 return NfcState::NotSupported; 386 return NfcState::NotSupported;
361 } 387 }
388
389 virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request,
390 [[maybe_unused]] MifareRequest& out_data) {
391 return NfcState::NotSupported;
392 }
393
394 virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) {
395 return NfcState::NotSupported;
396 }
362}; 397};
363 398
364/// An abstract class template for a factory that can create input devices. 399/// An abstract class template for a factory that can create input devices.
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 0a7777732..c937495f9 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -149,12 +149,16 @@ void EmulatedController::LoadDevices() {
149 149
150 camera_params[0] = right_joycon; 150 camera_params[0] = right_joycon;
151 camera_params[0].Set("camera", true); 151 camera_params[0].Set("camera", true);
152 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
153 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
154 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
155 nfc_params[1] = right_joycon; 152 nfc_params[1] = right_joycon;
156 nfc_params[1].Set("nfc", true); 153 nfc_params[1].Set("nfc", true);
157 154
155 // Only map virtual devices to the first controller
156 if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) {
157 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
158 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
159 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
160 }
161
158 output_params[LeftIndex] = left_joycon; 162 output_params[LeftIndex] = left_joycon;
159 output_params[RightIndex] = right_joycon; 163 output_params[RightIndex] = right_joycon;
160 output_params[2] = camera_params[1]; 164 output_params[2] = camera_params[1];
@@ -1176,10 +1180,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1176 return; 1180 return;
1177 } 1181 }
1178 1182
1179 controller.nfc_state = { 1183 controller.nfc_state = controller.nfc_values;
1180 controller.nfc_values.state,
1181 controller.nfc_values.data,
1182 };
1183} 1184}
1184 1185
1185bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1186bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1308,6 +1309,73 @@ bool EmulatedController::HasNfc() const {
1308 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); 1309 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
1309} 1310}
1310 1311
1312bool EmulatedController::AddNfcHandle() {
1313 nfc_handles++;
1314 return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) ==
1315 Common::Input::DriverResult::Success;
1316}
1317
1318bool EmulatedController::RemoveNfcHandle() {
1319 nfc_handles--;
1320 if (nfc_handles <= 0) {
1321 return SetPollingMode(EmulatedDeviceIndex::RightIndex,
1322 Common::Input::PollingMode::Active) ==
1323 Common::Input::DriverResult::Success;
1324 }
1325 return true;
1326}
1327
1328bool EmulatedController::StartNfcPolling() {
1329 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1330 auto& nfc_virtual_output_device = output_devices[3];
1331
1332 return nfc_output_device->StartNfcPolling() == Common::Input::NfcState::Success ||
1333 nfc_virtual_output_device->StartNfcPolling() == Common::Input::NfcState::Success;
1334}
1335
1336bool EmulatedController::StopNfcPolling() {
1337 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1338 auto& nfc_virtual_output_device = output_devices[3];
1339
1340 return nfc_output_device->StopNfcPolling() == Common::Input::NfcState::Success ||
1341 nfc_virtual_output_device->StopNfcPolling() == Common::Input::NfcState::Success;
1342}
1343
1344bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1345 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1346 auto& nfc_virtual_output_device = output_devices[3];
1347
1348 if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) {
1349 return true;
1350 }
1351
1352 return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success;
1353}
1354
1355bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1356 Common::Input::MifareRequest& out_data) {
1357 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1358 auto& nfc_virtual_output_device = output_devices[3];
1359
1360 if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) {
1361 return true;
1362 }
1363
1364 return nfc_virtual_output_device->ReadMifareData(request, out_data) ==
1365 Common::Input::NfcState::Success;
1366}
1367
1368bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1369 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1370 auto& nfc_virtual_output_device = output_devices[3];
1371
1372 if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) {
1373 return true;
1374 }
1375
1376 return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success;
1377}
1378
1311bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1379bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1312 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1380 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1313 auto& nfc_virtual_output_device = output_devices[3]; 1381 auto& nfc_virtual_output_device = output_devices[3];
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 09fe1a0ab..d511e5fac 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -97,10 +97,7 @@ struct RingSensorForce {
97 f32 force; 97 f32 force;
98}; 98};
99 99
100struct NfcState { 100using NfcState = Common::Input::NfcStatus;
101 Common::Input::NfcState state{};
102 std::vector<u8> data{};
103};
104 101
105struct ControllerMotion { 102struct ControllerMotion {
106 Common::Vec3f accel{}; 103 Common::Vec3f accel{};
@@ -393,9 +390,31 @@ public:
393 /// Returns true if the device has nfc support 390 /// Returns true if the device has nfc support
394 bool HasNfc() const; 391 bool HasNfc() const;
395 392
393 /// Sets the joycon in nfc mode and increments the handle count
394 bool AddNfcHandle();
395
396 /// Decrements the handle count if zero sets the joycon in active mode
397 bool RemoveNfcHandle();
398
399 /// Start searching for nfc tags
400 bool StartNfcPolling();
401
402 /// Stop searching for nfc tags
403 bool StopNfcPolling();
404
405 /// Returns true if the nfc tag was readable
406 bool ReadAmiiboData(std::vector<u8>& data);
407
396 /// Returns true if the nfc tag was written 408 /// Returns true if the nfc tag was written
397 bool WriteNfc(const std::vector<u8>& data); 409 bool WriteNfc(const std::vector<u8>& data);
398 410
411 /// Returns true if the nfc tag was readable
412 bool ReadMifareData(const Common::Input::MifareRequest& request,
413 Common::Input::MifareRequest& out_data);
414
415 /// Returns true if the nfc tag was written
416 bool WriteMifareData(const Common::Input::MifareRequest& request);
417
399 /// Returns the led pattern corresponding to this emulated controller 418 /// Returns the led pattern corresponding to this emulated controller
400 LedPattern GetLedPattern() const; 419 LedPattern GetLedPattern() const;
401 420
@@ -532,6 +551,7 @@ private:
532 bool system_buttons_enabled{true}; 551 bool system_buttons_enabled{true};
533 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 552 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
534 u32 turbo_button_state{0}; 553 u32 turbo_button_state{0};
554 std::size_t nfc_handles{0};
535 555
536 // Temporary values to avoid doing changes while the controller is in configuring mode 556 // Temporary values to avoid doing changes while the controller is in configuring mode
537 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 557 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 4ccb1c596..a05716fd8 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -299,11 +299,7 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal
299 Common::Input::NfcStatus nfc{}; 299 Common::Input::NfcStatus nfc{};
300 switch (callback.type) { 300 switch (callback.type) {
301 case Common::Input::InputType::Nfc: 301 case Common::Input::InputType::Nfc:
302 nfc = { 302 return callback.nfc_status;
303 .state = callback.nfc_status,
304 .data = callback.raw_data,
305 };
306 break;
307 default: 303 default:
308 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); 304 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
309 break; 305 break;
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index 8b754e9d4..19ed184e8 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -141,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
141 applet_output.device_handle = applet_input_common.device_handle; 141 applet_output.device_handle = applet_input_common.device_handle;
142 applet_output.result = CabinetResult::Cancel; 142 applet_output.result = CabinetResult::Cancel;
143 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); 143 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
144 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false); 144 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
145 nfp_device->Finalize(); 145 nfp_device->Finalize();
146 146
147 if (reg_result.IsSuccess()) { 147 if (reg_result.IsSuccess()) {
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index f4b180b06..5bf289818 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -93,7 +93,8 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
93 const auto nfc_status = npad_device->GetNfc(); 93 const auto nfc_status = npad_device->GetNfc();
94 switch (nfc_status.state) { 94 switch (nfc_status.state) {
95 case Common::Input::NfcState::NewAmiibo: 95 case Common::Input::NfcState::NewAmiibo:
96 LoadNfcTag(nfc_status.data); 96 LoadNfcTag(nfc_status.protocol, nfc_status.tag_type, nfc_status.uuid_length,
97 nfc_status.uuid);
97 break; 98 break;
98 case Common::Input::NfcState::AmiiboRemoved: 99 case Common::Input::NfcState::AmiiboRemoved:
99 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { 100 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
@@ -108,28 +109,46 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
108 } 109 }
109} 110}
110 111
111bool NfcDevice::LoadNfcTag(std::span<const u8> data) { 112bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid) {
112 if (device_state != DeviceState::SearchingForTag) { 113 if (device_state != DeviceState::SearchingForTag) {
113 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state); 114 LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
114 return false; 115 return false;
115 } 116 }
116 117
118 if ((protocol & static_cast<u8>(allowed_protocols)) == 0) {
119 LOG_ERROR(Service_NFC, "Protocol not supported {}", protocol);
120 return false;
121 }
122
123 real_tag_info = {
124 .uuid = uuid,
125 .uuid_length = uuid_length,
126 .protocol = static_cast<NfcProtocol>(protocol),
127 .tag_type = static_cast<TagType>(tag_type),
128 };
129
130 device_state = DeviceState::TagFound;
131 deactivate_event->GetReadableEvent().Clear();
132 activate_event->Signal();
133 return true;
134}
135
136bool NfcDevice::LoadAmiiboData() {
137 std::vector<u8> data{};
138
139 if (!npad_device->ReadAmiiboData(data)) {
140 return false;
141 }
142
117 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { 143 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
118 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); 144 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
119 return false; 145 return false;
120 } 146 }
121 147
122 mifare_data.resize(data.size());
123 memcpy(mifare_data.data(), data.data(), data.size());
124
125 memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); 148 memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
126 is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data); 149 is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data);
127 is_write_protected = false; 150 is_write_protected = false;
128 151
129 device_state = DeviceState::TagFound;
130 deactivate_event->GetReadableEvent().Clear();
131 activate_event->Signal();
132
133 // Fallback for plain amiibos 152 // Fallback for plain amiibos
134 if (is_plain_amiibo) { 153 if (is_plain_amiibo) {
135 LOG_INFO(Service_NFP, "Using plain amiibo"); 154 LOG_INFO(Service_NFP, "Using plain amiibo");
@@ -147,6 +166,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
147 return true; 166 return true;
148 } 167 }
149 168
169 LOG_INFO(Service_NFP, "Using encrypted amiibo");
150 tag_data = {}; 170 tag_data = {};
151 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); 171 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
152 return true; 172 return true;
@@ -162,7 +182,6 @@ void NfcDevice::CloseNfcTag() {
162 device_state = DeviceState::TagRemoved; 182 device_state = DeviceState::TagRemoved;
163 encrypted_tag_data = {}; 183 encrypted_tag_data = {};
164 tag_data = {}; 184 tag_data = {};
165 mifare_data = {};
166 activate_event->GetReadableEvent().Clear(); 185 activate_event->GetReadableEvent().Clear();
167 deactivate_event->Signal(); 186 deactivate_event->Signal();
168} 187}
@@ -179,8 +198,12 @@ void NfcDevice::Initialize() {
179 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; 198 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
180 encrypted_tag_data = {}; 199 encrypted_tag_data = {};
181 tag_data = {}; 200 tag_data = {};
182 mifare_data = {}; 201
183 is_initalized = true; 202 if (device_state != DeviceState::Initialized) {
203 return;
204 }
205
206 is_initalized = npad_device->AddNfcHandle();
184} 207}
185 208
186void NfcDevice::Finalize() { 209void NfcDevice::Finalize() {
@@ -190,6 +213,11 @@ void NfcDevice::Finalize() {
190 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { 213 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
191 StopDetection(); 214 StopDetection();
192 } 215 }
216
217 if (device_state != DeviceState::Unavailable) {
218 npad_device->RemoveNfcHandle();
219 }
220
193 device_state = DeviceState::Unavailable; 221 device_state = DeviceState::Unavailable;
194 is_initalized = false; 222 is_initalized = false;
195} 223}
@@ -200,10 +228,8 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
200 return ResultWrongDeviceState; 228 return ResultWrongDeviceState;
201 } 229 }
202 230
203 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, 231 if (!npad_device->StartNfcPolling()) {
204 Common::Input::PollingMode::NFC) != 232 LOG_ERROR(Service_NFC, "Nfc polling not supported");
205 Common::Input::DriverResult::Success) {
206 LOG_ERROR(Service_NFC, "Nfc not supported");
207 return ResultNfcDisabled; 233 return ResultNfcDisabled;
208 } 234 }
209 235
@@ -213,9 +239,6 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
213} 239}
214 240
215Result NfcDevice::StopDetection() { 241Result NfcDevice::StopDetection() {
216 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
217 Common::Input::PollingMode::Active);
218
219 if (device_state == DeviceState::Initialized) { 242 if (device_state == DeviceState::Initialized) {
220 return ResultSuccess; 243 return ResultSuccess;
221 } 244 }
@@ -225,6 +248,7 @@ Result NfcDevice::StopDetection() {
225 } 248 }
226 249
227 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { 250 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
251 npad_device->StopNfcPolling();
228 device_state = DeviceState::Initialized; 252 device_state = DeviceState::Initialized;
229 return ResultSuccess; 253 return ResultSuccess;
230 } 254 }
@@ -233,7 +257,7 @@ Result NfcDevice::StopDetection() {
233 return ResultWrongDeviceState; 257 return ResultWrongDeviceState;
234} 258}
235 259
236Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { 260Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
237 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { 261 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
238 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 262 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
239 if (device_state == DeviceState::TagRemoved) { 263 if (device_state == DeviceState::TagRemoved) {
@@ -242,41 +266,15 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
242 return ResultWrongDeviceState; 266 return ResultWrongDeviceState;
243 } 267 }
244 268
245 UniqueSerialNumber uuid{}; 269 tag_info = real_tag_info;
246 u8 uuid_length{};
247 NfcProtocol protocol{NfcProtocol::TypeA};
248 TagType tag_type{TagType::Type2};
249
250 if (is_mifare) {
251 tag_type = TagType::Mifare;
252 uuid_length = sizeof(NFP::NtagTagUuid);
253 memcpy(uuid.data(), mifare_data.data(), uuid_length);
254 } else {
255 tag_type = TagType::Type2;
256 uuid_length = sizeof(NFP::NtagTagUuid);
257 NFP::NtagTagUuid nUuid{
258 .part1 = encrypted_tag_data.uuid.part1,
259 .part2 = encrypted_tag_data.uuid.part2,
260 .nintendo_id = encrypted_tag_data.uuid.nintendo_id,
261 };
262 memcpy(uuid.data(), &nUuid, uuid_length);
263 270
264 // Generate random UUID to bypass amiibo load limits 271 // Generate random UUID to bypass amiibo load limits
265 if (Settings::values.random_amiibo_id) { 272 if (real_tag_info.tag_type == TagType::Type2 && Settings::values.random_amiibo_id) {
266 Common::TinyMT rng{}; 273 Common::TinyMT rng{};
267 rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); 274 rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
268 rng.GenerateRandomBytes(uuid.data(), uuid_length); 275 rng.GenerateRandomBytes(tag_info.uuid.data(), tag_info.uuid_length);
269 }
270 } 276 }
271 277
272 // Protocol and tag type may change here
273 tag_info = {
274 .uuid = uuid,
275 .uuid_length = uuid_length,
276 .protocol = protocol,
277 .tag_type = tag_type,
278 };
279
280 return ResultSuccess; 278 return ResultSuccess;
281} 279}
282 280
@@ -293,7 +291,7 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter
293 Result result = ResultSuccess; 291 Result result = ResultSuccess;
294 292
295 TagInfo tag_info{}; 293 TagInfo tag_info{};
296 result = GetTagInfo(tag_info, true); 294 result = GetTagInfo(tag_info);
297 295
298 if (result.IsError()) { 296 if (result.IsError()) {
299 return result; 297 return result;
@@ -307,6 +305,8 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter
307 return ResultInvalidArgument; 305 return ResultInvalidArgument;
308 } 306 }
309 307
308 Common::Input::MifareRequest request{};
309 Common::Input::MifareRequest out_data{};
310 const auto unknown = parameters[0].sector_key.unknown; 310 const auto unknown = parameters[0].sector_key.unknown;
311 for (std::size_t i = 0; i < parameters.size(); i++) { 311 for (std::size_t i = 0; i < parameters.size(); i++) {
312 if (unknown != parameters[i].sector_key.unknown) { 312 if (unknown != parameters[i].sector_key.unknown) {
@@ -315,25 +315,29 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter
315 } 315 }
316 316
317 for (std::size_t i = 0; i < parameters.size(); i++) { 317 for (std::size_t i = 0; i < parameters.size(); i++) {
318 result = ReadMifare(parameters[i], read_block_data[i]); 318 if (parameters[i].sector_key.command == MifareCmd::None) {
319 if (result.IsError()) { 319 continue;
320 break;
321 } 320 }
321 request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
322 request.data[i].sector = parameters[i].sector_number;
323 memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
324 sizeof(KeyData));
322 } 325 }
323 326
324 return result; 327 if (!npad_device->ReadMifareData(request, out_data)) {
325}
326
327Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
328 MifareReadBlockData& read_block_data) const {
329 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
330 read_block_data.sector_number = parameter.sector_number;
331 if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
332 return ResultMifareError288; 328 return ResultMifareError288;
333 } 329 }
334 330
335 // TODO: Use parameter.sector_key to read encrypted data 331 for (std::size_t i = 0; i < read_block_data.size(); i++) {
336 memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock)); 332 if (static_cast<MifareCmd>(out_data.data[i].command) == MifareCmd::None) {
333 continue;
334 }
335
336 read_block_data[i] = {
337 .data = out_data.data[i].data,
338 .sector_number = out_data.data[i].sector,
339 };
340 }
337 341
338 return ResultSuccess; 342 return ResultSuccess;
339} 343}
@@ -342,7 +346,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
342 Result result = ResultSuccess; 346 Result result = ResultSuccess;
343 347
344 TagInfo tag_info{}; 348 TagInfo tag_info{};
345 result = GetTagInfo(tag_info, true); 349 result = GetTagInfo(tag_info);
346 350
347 if (result.IsError()) { 351 if (result.IsError()) {
348 return result; 352 return result;
@@ -363,42 +367,25 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
363 } 367 }
364 } 368 }
365 369
370 Common::Input::MifareRequest request{};
366 for (std::size_t i = 0; i < parameters.size(); i++) { 371 for (std::size_t i = 0; i < parameters.size(); i++) {
367 result = WriteMifare(parameters[i]); 372 if (parameters[i].sector_key.command == MifareCmd::None) {
368 if (result.IsError()) { 373 continue;
369 break;
370 } 374 }
375 request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
376 request.data[i].sector = parameters[i].sector_number;
377 memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
378 sizeof(KeyData));
379 memcpy(request.data[i].data.data(), parameters[i].data.data(), sizeof(KeyData));
371 } 380 }
372 381
373 if (!npad_device->WriteNfc(mifare_data)) { 382 if (!npad_device->WriteMifareData(request)) {
374 LOG_ERROR(Service_NFP, "Error writing to file");
375 return ResultMifareError288; 383 return ResultMifareError288;
376 } 384 }
377 385
378 return result; 386 return result;
379} 387}
380 388
381Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) {
382 const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
383
384 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
385 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
386 if (device_state == DeviceState::TagRemoved) {
387 return ResultTagRemoved;
388 }
389 return ResultWrongDeviceState;
390 }
391
392 if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
393 return ResultMifareError288;
394 }
395
396 // TODO: Use parameter.sector_key to encrypt the data
397 memcpy(mifare_data.data() + sector_index, parameter.data.data(), sizeof(DataBlock));
398
399 return ResultSuccess;
400}
401
402Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 389Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
403 std::span<const u8> command_data, 390 std::span<const u8> command_data,
404 std::span<u8> out_data) { 391 std::span<u8> out_data) {
@@ -412,6 +399,11 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
412 return ResultWrongDeviceState; 399 return ResultWrongDeviceState;
413 } 400 }
414 401
402 if (!LoadAmiiboData()) {
403 LOG_ERROR(Service_NFP, "Not an amiibo");
404 return ResultInvalidTagType;
405 }
406
415 if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { 407 if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
416 LOG_ERROR(Service_NFP, "Not an amiibo"); 408 LOG_ERROR(Service_NFP, "Not an amiibo");
417 return ResultInvalidTagType; 409 return ResultInvalidTagType;
@@ -562,7 +554,7 @@ Result NfcDevice::Restore() {
562 554
563 NFC::TagInfo tag_info{}; 555 NFC::TagInfo tag_info{};
564 std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; 556 std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{};
565 Result result = GetTagInfo(tag_info, false); 557 Result result = GetTagInfo(tag_info);
566 558
567 if (result.IsError()) { 559 if (result.IsError()) {
568 return result; 560 return result;
@@ -635,7 +627,7 @@ Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const {
635 // TODO: Validate this data 627 // TODO: Validate this data
636 common_info = { 628 common_info = {
637 .last_write_date = settings.write_date.GetWriteDate(), 629 .last_write_date = settings.write_date.GetWriteDate(),
638 .write_counter = tag_data.write_counter, 630 .write_counter = tag_data.application_write_counter,
639 .version = tag_data.amiibo_version, 631 .version = tag_data.amiibo_version,
640 .application_area_size = sizeof(NFP::ApplicationArea), 632 .application_area_size = sizeof(NFP::ApplicationArea),
641 }; 633 };
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 7560210d6..0ed1ff34c 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -42,15 +42,12 @@ public:
42 Result StartDetection(NfcProtocol allowed_protocol); 42 Result StartDetection(NfcProtocol allowed_protocol);
43 Result StopDetection(); 43 Result StopDetection();
44 44
45 Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const; 45 Result GetTagInfo(TagInfo& tag_info) const;
46 46
47 Result ReadMifare(std::span<const MifareReadBlockParameter> parameters, 47 Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,
48 std::span<MifareReadBlockData> read_block_data) const; 48 std::span<MifareReadBlockData> read_block_data) const;
49 Result ReadMifare(const MifareReadBlockParameter& parameter,
50 MifareReadBlockData& read_block_data) const;
51 49
52 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); 50 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
53 Result WriteMifare(const MifareWriteBlockParameter& parameter);
54 51
55 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 52 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
56 std::span<const u8> command_data, std::span<u8> out_data); 53 std::span<const u8> command_data, std::span<u8> out_data);
@@ -105,7 +102,8 @@ public:
105 102
106private: 103private:
107 void NpadUpdate(Core::HID::ControllerTriggerType type); 104 void NpadUpdate(Core::HID::ControllerTriggerType type);
108 bool LoadNfcTag(std::span<const u8> data); 105 bool LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid);
106 bool LoadAmiiboData();
109 void CloseNfcTag(); 107 void CloseNfcTag();
110 108
111 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; 109 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
@@ -140,8 +138,8 @@ private:
140 bool is_write_protected{}; 138 bool is_write_protected{};
141 NFP::MountTarget mount_target{NFP::MountTarget::None}; 139 NFP::MountTarget mount_target{NFP::MountTarget::None};
142 140
141 TagInfo real_tag_info{};
143 NFP::NTAG215File tag_data{}; 142 NFP::NTAG215File tag_data{};
144 std::vector<u8> mifare_data{};
145 NFP::EncryptedNTAG215File encrypted_tag_data{}; 143 NFP::EncryptedNTAG215File encrypted_tag_data{};
146}; 144};
147 145
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index b0456508e..562f3a28e 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -29,6 +29,9 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex
29} 29}
30 30
31DeviceManager ::~DeviceManager() { 31DeviceManager ::~DeviceManager() {
32 if (is_initialized) {
33 Finalize();
34 }
32 service_context.CloseEvent(availability_change_event); 35 service_context.CloseEvent(availability_change_event);
33} 36}
34 37
@@ -125,14 +128,14 @@ Result DeviceManager::StopDetection(u64 device_handle) {
125 return result; 128 return result;
126} 129}
127 130
128Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const { 131Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const {
129 std::scoped_lock lock{mutex}; 132 std::scoped_lock lock{mutex};
130 133
131 std::shared_ptr<NfcDevice> device = nullptr; 134 std::shared_ptr<NfcDevice> device = nullptr;
132 auto result = GetDeviceHandle(device_handle, device); 135 auto result = GetDeviceHandle(device_handle, device);
133 136
134 if (result.IsSuccess()) { 137 if (result.IsSuccess()) {
135 result = device->GetTagInfo(tag_info, is_mifare); 138 result = device->GetTagInfo(tag_info);
136 result = VerifyDeviceResult(device, result); 139 result = VerifyDeviceResult(device, result);
137 } 140 }
138 141
@@ -546,7 +549,7 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
546 NFC::TagInfo tag_info{}; 549 NFC::TagInfo tag_info{};
547 550
548 if (result.IsSuccess()) { 551 if (result.IsSuccess()) {
549 result = device->GetTagInfo(tag_info, false); 552 result = device->GetTagInfo(tag_info);
550 } 553 }
551 554
552 if (result.IsSuccess()) { 555 if (result.IsSuccess()) {
@@ -565,7 +568,7 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
565 NFC::TagInfo tag_info{}; 568 NFC::TagInfo tag_info{};
566 569
567 if (result.IsSuccess()) { 570 if (result.IsSuccess()) {
568 result = device->GetTagInfo(tag_info, false); 571 result = device->GetTagInfo(tag_info);
569 } 572 }
570 573
571 if (result.IsSuccess()) { 574 if (result.IsSuccess()) {
diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h
index 2971e280f..c61ba0cf3 100644
--- a/src/core/hle/service/nfc/common/device_manager.h
+++ b/src/core/hle/service/nfc/common/device_manager.h
@@ -33,7 +33,7 @@ public:
33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const; 33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol); 34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
35 Result StopDetection(u64 device_handle); 35 Result StopDetection(u64 device_handle);
36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const; 36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const;
37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const; 37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;
38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const; 38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;
39 Result ReadMifare(u64 device_handle, 39 Result ReadMifare(u64 device_handle,
diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h
index 75b59f021..467937399 100644
--- a/src/core/hle/service/nfc/mifare_types.h
+++ b/src/core/hle/service/nfc/mifare_types.h
@@ -11,9 +11,10 @@
11namespace Service::NFC { 11namespace Service::NFC {
12 12
13enum class MifareCmd : u8 { 13enum class MifareCmd : u8 {
14 None = 0x00,
15 Read = 0x30,
14 AuthA = 0x60, 16 AuthA = 0x60,
15 AuthB = 0x61, 17 AuthB = 0x61,
16 Read = 0x30,
17 Write = 0xA0, 18 Write = 0xA0,
18 Transfer = 0xB0, 19 Transfer = 0xB0,
19 Decrement = 0xC0, 20 Decrement = 0xC0,
@@ -35,17 +36,17 @@ static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
35 36
36// This is nn::nfc::MifareReadBlockParameter 37// This is nn::nfc::MifareReadBlockParameter
37struct MifareReadBlockParameter { 38struct MifareReadBlockParameter {
38 u8 sector_number; 39 u8 sector_number{};
39 INSERT_PADDING_BYTES(0x7); 40 INSERT_PADDING_BYTES(0x7);
40 SectorKey sector_key; 41 SectorKey sector_key{};
41}; 42};
42static_assert(sizeof(MifareReadBlockParameter) == 0x18, 43static_assert(sizeof(MifareReadBlockParameter) == 0x18,
43 "MifareReadBlockParameter is an invalid size"); 44 "MifareReadBlockParameter is an invalid size");
44 45
45// This is nn::nfc::MifareReadBlockData 46// This is nn::nfc::MifareReadBlockData
46struct MifareReadBlockData { 47struct MifareReadBlockData {
47 DataBlock data; 48 DataBlock data{};
48 u8 sector_number; 49 u8 sector_number{};
49 INSERT_PADDING_BYTES(0x7); 50 INSERT_PADDING_BYTES(0x7);
50}; 51};
51static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); 52static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index 130fb7f78..e7ca7582e 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -174,8 +174,7 @@ void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
174 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); 174 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
175 175
176 TagInfo tag_info{}; 176 TagInfo tag_info{};
177 auto result = 177 auto result = GetManager()->GetTagInfo(device_handle, tag_info);
178 GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare);
179 result = TranslateResultToServiceError(result); 178 result = TranslateResultToServiceError(result);
180 179
181 if (result.IsSuccess()) { 180 if (result.IsSuccess()) {
@@ -216,8 +215,8 @@ void NfcInterface::ReadMifare(HLERequestContext& ctx) {
216 memcpy(read_commands.data(), buffer.data(), 215 memcpy(read_commands.data(), buffer.data(),
217 number_of_commands * sizeof(MifareReadBlockParameter)); 216 number_of_commands * sizeof(MifareReadBlockParameter));
218 217
219 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", 218 LOG_INFO(Service_NFC, "called, device_handle={}, read_commands_size={}", device_handle,
220 device_handle, number_of_commands); 219 number_of_commands);
221 220
222 std::vector<MifareReadBlockData> out_data(number_of_commands); 221 std::vector<MifareReadBlockData> out_data(number_of_commands);
223 auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data); 222 auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index b2b5677c8..52494e0d9 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
195 OnMotionUpdate(port, type, id, value); 195 OnMotionUpdate(port, type, id, value);
196 }}, 196 }},
197 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, 197 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
198 .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) { 198 .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) {
199 OnAmiiboUpdate(port, type, amiibo_data); 199 OnAmiiboUpdate(port, type, tag_info);
200 }}, 200 }},
201 .on_camera_data = {[this, port](const std::vector<u8>& camera_data, 201 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
202 Joycon::IrsResolution format) { 202 Joycon::IrsResolution format) {
@@ -291,13 +291,105 @@ 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::StartNfcPolling(const PadIdentifier& identifier) {
295 auto handle = GetHandle(identifier);
296 if (handle == nullptr) {
297 return Common::Input::NfcState::Unknown;
298 }
299 return TranslateDriverResult(handle->StartNfcPolling());
300};
301
302Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) {
303 auto handle = GetHandle(identifier);
304 if (handle == nullptr) {
305 return Common::Input::NfcState::Unknown;
306 }
307 return TranslateDriverResult(handle->StopNfcPolling());
308};
309
310Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier,
311 std::vector<u8>& out_data) {
312 auto handle = GetHandle(identifier);
313 if (handle == nullptr) {
314 return Common::Input::NfcState::Unknown;
315 }
316 return TranslateDriverResult(handle->ReadAmiiboData(out_data));
317}
318
294Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier, 319Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
295 const std::vector<u8>& data) { 320 const std::vector<u8>& data) {
296 auto handle = GetHandle(identifier); 321 auto handle = GetHandle(identifier);
297 if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { 322 if (handle == nullptr) {
298 return Common::Input::NfcState::WriteFailed; 323 return Common::Input::NfcState::Unknown;
299 } 324 }
300 return Common::Input::NfcState::Success; 325 return TranslateDriverResult(handle->WriteNfcData(data));
326};
327
328Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
329 const Common::Input::MifareRequest& request,
330 Common::Input::MifareRequest& data) {
331 auto handle = GetHandle(identifier);
332 if (handle == nullptr) {
333 return Common::Input::NfcState::Unknown;
334 }
335
336 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
337 std::vector<Joycon::MifareReadChunk> read_request{};
338 for (const auto& request_data : request.data) {
339 if (request_data.command == 0) {
340 continue;
341 }
342 Joycon::MifareReadChunk chunk = {
343 .command = command,
344 .sector_key = {},
345 .sector = request_data.sector,
346 };
347 memcpy(chunk.sector_key.data(), request_data.key.data(),
348 sizeof(Joycon::MifareReadChunk::sector_key));
349 read_request.emplace_back(chunk);
350 }
351
352 std::vector<Joycon::MifareReadData> read_data(read_request.size());
353 const auto result = handle->ReadMifareData(read_request, read_data);
354 if (result == Joycon::DriverResult::Success) {
355 for (std::size_t i = 0; i < read_request.size(); i++) {
356 data.data[i] = {
357 .command = static_cast<u8>(command),
358 .sector = read_data[i].sector,
359 .key = {},
360 .data = read_data[i].data,
361 };
362 }
363 }
364 return TranslateDriverResult(result);
365};
366
367Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier,
368 const Common::Input::MifareRequest& request) {
369 auto handle = GetHandle(identifier);
370 if (handle == nullptr) {
371 return Common::Input::NfcState::Unknown;
372 }
373
374 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
375 std::vector<Joycon::MifareWriteChunk> write_request{};
376 for (const auto& request_data : request.data) {
377 if (request_data.command == 0) {
378 continue;
379 }
380 Joycon::MifareWriteChunk chunk = {
381 .command = command,
382 .sector_key = {},
383 .sector = request_data.sector,
384 .data = {},
385 };
386 memcpy(chunk.sector_key.data(), request_data.key.data(),
387 sizeof(Joycon::MifareReadChunk::sector_key));
388 memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data));
389 write_request.emplace_back(chunk);
390 }
391
392 return TranslateDriverResult(handle->WriteMifareData(write_request));
301}; 393};
302 394
303Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, 395Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
@@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
403} 495}
404 496
405void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 497void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
406 const std::vector<u8>& amiibo_data) { 498 const Joycon::TagInfo& tag_info) {
407 const auto identifier = GetIdentifier(port, type); 499 const auto identifier = GetIdentifier(port, type);
408 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved 500 const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved
409 : Common::Input::NfcState::NewAmiibo; 501 : Common::Input::NfcState::NewAmiibo;
410 SetNfc(identifier, {nfc_state, amiibo_data}); 502
503 const Common::Input::NfcStatus nfc_status{
504 .state = nfc_state,
505 .uuid_length = tag_info.uuid_length,
506 .protocol = tag_info.protocol,
507 .tag_type = tag_info.tag_type,
508 .uuid = tag_info.uuid,
509 };
510
511 SetNfc(identifier, nfc_status);
411} 512}
412 513
413void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 514void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
@@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
726 return "Unknown Switch Controller"; 827 return "Unknown Switch Controller";
727 } 828 }
728} 829}
830
831Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const {
832 switch (result) {
833 case Joycon::DriverResult::Success:
834 return Common::Input::NfcState::Success;
835 case Joycon::DriverResult::Disabled:
836 return Common::Input::NfcState::WrongDeviceState;
837 case Joycon::DriverResult::NotSupported:
838 return Common::Input::NfcState::NotSupported;
839 default:
840 return Common::Input::NfcState::Unknown;
841 }
842}
843
729} // namespace InputCommon 844} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index e3f0ad78f..4c323d7d6 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -15,6 +15,7 @@ using SerialNumber = std::array<u8, 15>;
15struct Battery; 15struct Battery;
16struct Color; 16struct Color;
17struct MotionData; 17struct MotionData;
18struct TagInfo;
18enum class ControllerType : u8; 19enum class ControllerType : u8;
19enum class DriverResult; 20enum class DriverResult;
20enum class IrsResolution; 21enum class IrsResolution;
@@ -39,9 +40,18 @@ public:
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, 40 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override; 41 Common::Input::CameraFormat camera_format) override;
41 42
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 43 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 44 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override;
45 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override;
46 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier,
47 std::vector<u8>& out_data) override;
48 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,
44 const std::vector<u8>& data) override; 49 const std::vector<u8>& data) override;
50 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier,
51 const Common::Input::MifareRequest& request,
52 Common::Input::MifareRequest& out_data) override;
53 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier,
54 const Common::Input::MifareRequest& request) override;
45 55
46 Common::Input::DriverResult SetPollingMode( 56 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; 57 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
@@ -82,7 +92,7 @@ private:
82 const Joycon::MotionData& value); 92 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data); 93 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, 94 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
85 const std::vector<u8>& amiibo_data); 95 const Joycon::TagInfo& amiibo_data);
86 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, 96 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
87 Joycon::IrsResolution format); 97 Joycon::IrsResolution format);
88 98
@@ -102,6 +112,8 @@ private:
102 /// Returns the name of the device in text format 112 /// Returns the name of the device in text format
103 std::string JoyconName(Joycon::ControllerType type) const; 113 std::string JoyconName(Joycon::ControllerType type) const;
104 114
115 Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const;
116
105 std::jthread scan_thread; 117 std::jthread scan_thread;
106 118
107 // Joycon types are split by type to ease supporting dualjoycon configurations 119 // Joycon types are split by type to ease supporting dualjoycon configurations
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 6435b8af8..180eb53ef 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -29,14 +29,13 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
29 29
30 switch (polling_mode) { 30 switch (polling_mode) {
31 case Common::Input::PollingMode::NFC: 31 case Common::Input::PollingMode::NFC:
32 if (state == State::Initialized) { 32 state = State::Initialized;
33 state = State::WaitingForAmiibo;
34 }
35 return Common::Input::DriverResult::Success; 33 return Common::Input::DriverResult::Success;
36 default: 34 default:
37 if (state == State::AmiiboIsOpen) { 35 if (state == State::TagNearby) {
38 CloseAmiibo(); 36 CloseAmiibo();
39 } 37 }
38 state = State::Disabled;
40 return Common::Input::DriverResult::NotSupported; 39 return Common::Input::DriverResult::NotSupported;
41 } 40 }
42} 41}
@@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(
45 [[maybe_unused]] const PadIdentifier& identifier_) const { 44 [[maybe_unused]] const PadIdentifier& identifier_) const {
46 return Common::Input::NfcState::Success; 45 return Common::Input::NfcState::Success;
47} 46}
47Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) {
48 if (state != State::Initialized) {
49 return Common::Input::NfcState::WrongDeviceState;
50 }
51 state = State::WaitingForAmiibo;
52 return Common::Input::NfcState::Success;
53}
54
55Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) {
56 if (state == State::Disabled) {
57 return Common::Input::NfcState::WrongDeviceState;
58 }
59 if (state == State::TagNearby) {
60 CloseAmiibo();
61 }
62 state = State::Initialized;
63 return Common::Input::NfcState::Success;
64}
65
66Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_,
67 std::vector<u8>& out_data) {
68 if (state != State::TagNearby) {
69 return Common::Input::NfcState::WrongDeviceState;
70 }
71
72 if (status.tag_type != 1U << 1) {
73 return Common::Input::NfcState::InvalidTagType;
74 }
75
76 out_data.resize(nfc_data.size());
77 memcpy(out_data.data(), nfc_data.data(), nfc_data.size());
78 return Common::Input::NfcState::Success;
79}
48 80
49Common::Input::NfcState VirtualAmiibo::WriteNfcData( 81Common::Input::NfcState VirtualAmiibo::WriteNfcData(
50 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { 82 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
@@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
66 return Common::Input::NfcState::Success; 98 return Common::Input::NfcState::Success;
67} 99}
68 100
101Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_,
102 const Common::Input::MifareRequest& request,
103 Common::Input::MifareRequest& out_data) {
104 if (state != State::TagNearby) {
105 return Common::Input::NfcState::WrongDeviceState;
106 }
107
108 if (status.tag_type != 1U << 6) {
109 return Common::Input::NfcState::InvalidTagType;
110 }
111
112 for (std::size_t i = 0; i < request.data.size(); i++) {
113 if (request.data[i].command == 0) {
114 continue;
115 }
116 out_data.data[i].command = request.data[i].command;
117 out_data.data[i].sector = request.data[i].sector;
118
119 const std::size_t sector_index =
120 request.data[i].sector * sizeof(Common::Input::MifareData::data);
121
122 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
123 return Common::Input::NfcState::WriteFailed;
124 }
125
126 // Ignore the sector key as we don't support it
127 memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index,
128 sizeof(Common::Input::MifareData::data));
129 }
130
131 return Common::Input::NfcState::Success;
132}
133
134Common::Input::NfcState VirtualAmiibo::WriteMifareData(
135 const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) {
136 if (state != State::TagNearby) {
137 return Common::Input::NfcState::WrongDeviceState;
138 }
139
140 if (status.tag_type != 1U << 6) {
141 return Common::Input::NfcState::InvalidTagType;
142 }
143
144 for (std::size_t i = 0; i < request.data.size(); i++) {
145 if (request.data[i].command == 0) {
146 continue;
147 }
148
149 const std::size_t sector_index =
150 request.data[i].sector * sizeof(Common::Input::MifareData::data);
151
152 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
153 return Common::Input::NfcState::WriteFailed;
154 }
155
156 // Ignore the sector key as we don't support it
157 memcpy(nfc_data.data() + sector_index, request.data[i].data.data(),
158 sizeof(Common::Input::MifareData::data));
159 }
160
161 return Common::Input::NfcState::Success;
162}
163
69VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { 164VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
70 return state; 165 return state;
71} 166}
@@ -112,23 +207,31 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
112 case AmiiboSizeWithoutPassword: 207 case AmiiboSizeWithoutPassword:
113 case AmiiboSizeWithSignature: 208 case AmiiboSizeWithSignature:
114 nfc_data.resize(AmiiboSize); 209 nfc_data.resize(AmiiboSize);
210 status.tag_type = 1U << 1;
211 status.uuid_length = 7;
115 break; 212 break;
116 case MifareSize: 213 case MifareSize:
117 nfc_data.resize(MifareSize); 214 nfc_data.resize(MifareSize);
215 status.tag_type = 1U << 6;
216 status.uuid_length = 4;
118 break; 217 break;
119 default: 218 default:
120 return Info::NotAnAmiibo; 219 return Info::NotAnAmiibo;
121 } 220 }
122 221
123 state = State::AmiiboIsOpen; 222 status.uuid = {};
223 status.protocol = 1;
224 state = State::TagNearby;
225 status.state = Common::Input::NfcState::NewAmiibo,
124 memcpy(nfc_data.data(), data.data(), data.size_bytes()); 226 memcpy(nfc_data.data(), data.data(), data.size_bytes());
125 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 227 memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length);
228 SetNfc(identifier, status);
126 return Info::Success; 229 return Info::Success;
127} 230}
128 231
129VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { 232VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
130 if (state == State::AmiiboIsOpen) { 233 if (state == State::TagNearby) {
131 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 234 SetNfc(identifier, status);
132 return Info::Success; 235 return Info::Success;
133 } 236 }
134 237
@@ -136,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
136} 239}
137 240
138VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { 241VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
139 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo 242 if (state != State::TagNearby) {
140 : State::Initialized; 243 return Info::Success;
141 SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); 244 }
245
246 state = State::WaitingForAmiibo;
247 status.state = Common::Input::NfcState::AmiiboRemoved;
248 SetNfc(identifier, status);
249 status.tag_type = 0;
142 return Info::Success; 250 return Info::Success;
143} 251}
144 252
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 09ca09e68..490f38e05 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -20,9 +20,10 @@ namespace InputCommon {
20class VirtualAmiibo final : public InputEngine { 20class VirtualAmiibo final : public InputEngine {
21public: 21public:
22 enum class State { 22 enum class State {
23 Disabled,
23 Initialized, 24 Initialized,
24 WaitingForAmiibo, 25 WaitingForAmiibo,
25 AmiiboIsOpen, 26 TagNearby,
26 }; 27 };
27 28
28 enum class Info { 29 enum class Info {
@@ -41,9 +42,17 @@ public:
41 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 42 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
42 43
43 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 44 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
44 45 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override;
46 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override;
47 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_,
48 std::vector<u8>& out_data) override;
45 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 49 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
46 const std::vector<u8>& data) override; 50 const std::vector<u8>& data) override;
51 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_,
52 const Common::Input::MifareRequest& data,
53 Common::Input::MifareRequest& out_data) override;
54 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_,
55 const Common::Input::MifareRequest& data) override;
47 56
48 State GetCurrentState() const; 57 State GetCurrentState() const;
49 58
@@ -61,8 +70,9 @@ private:
61 static constexpr std::size_t MifareSize = 0x400; 70 static constexpr std::size_t MifareSize = 0x400;
62 71
63 std::string file_path{}; 72 std::string file_path{};
64 State state{State::Initialized}; 73 State state{State::Disabled};
65 std::vector<u8> nfc_data; 74 std::vector<u8> nfc_data;
75 Common::Input::NfcStatus status;
66 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive}; 76 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive};
67}; 77};
68} // namespace InputCommon 78} // namespace InputCommon
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 95106f16d..2c8c66951 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "common/scope_exit.h"
5#include "common/swap.h" 6#include "common/swap.h"
6#include "common/thread.h" 7#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h" 8#include "input_common/helpers/joycon_driver.h"
@@ -112,7 +113,7 @@ DriverResult JoyconDriver::InitializeDevice() {
112 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, 113 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
113 right_stick_calibration, motion_calibration); 114 right_stick_calibration, motion_calibration);
114 115
115 // Start pooling for data 116 // Start polling for data
116 is_connected = true; 117 is_connected = true;
117 if (!input_thread_running) { 118 if (!input_thread_running) {
118 input_thread = 119 input_thread =
@@ -208,7 +209,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
208 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); 209 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
209 } 210 }
210 211
211 if (nfc_protocol->IsEnabled()) { 212 if (nfc_protocol->IsPolling()) {
212 if (amiibo_detected) { 213 if (amiibo_detected) {
213 if (!nfc_protocol->HasAmiibo()) { 214 if (!nfc_protocol->HasAmiibo()) {
214 joycon_poller->UpdateAmiibo({}); 215 joycon_poller->UpdateAmiibo({});
@@ -218,10 +219,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
218 } 219 }
219 220
220 if (!amiibo_detected) { 221 if (!amiibo_detected) {
221 std::vector<u8> data(0x21C); 222 Joycon::TagInfo tag_info;
222 const auto result = nfc_protocol->ScanAmiibo(data); 223 const auto result = nfc_protocol->GetTagInfo(tag_info);
223 if (result == DriverResult::Success) { 224 if (result == DriverResult::Success) {
224 joycon_poller->UpdateAmiibo(data); 225 joycon_poller->UpdateAmiibo(tag_info);
225 amiibo_detected = true; 226 amiibo_detected = true;
226 } 227 }
227 } 228 }
@@ -247,6 +248,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
247} 248}
248 249
249DriverResult JoyconDriver::SetPollingMode() { 250DriverResult JoyconDriver::SetPollingMode() {
251 SCOPE_EXIT({ disable_input_thread = false; });
250 disable_input_thread = true; 252 disable_input_thread = true;
251 253
252 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); 254 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
@@ -276,7 +278,6 @@ DriverResult JoyconDriver::SetPollingMode() {
276 if (irs_enabled && supported_features.irs) { 278 if (irs_enabled && supported_features.irs) {
277 auto result = irs_protocol->EnableIrs(); 279 auto result = irs_protocol->EnableIrs();
278 if (result == DriverResult::Success) { 280 if (result == DriverResult::Success) {
279 disable_input_thread = false;
280 return result; 281 return result;
281 } 282 }
282 irs_protocol->DisableIrs(); 283 irs_protocol->DisableIrs();
@@ -286,10 +287,6 @@ DriverResult JoyconDriver::SetPollingMode() {
286 if (nfc_enabled && supported_features.nfc) { 287 if (nfc_enabled && supported_features.nfc) {
287 auto result = nfc_protocol->EnableNfc(); 288 auto result = nfc_protocol->EnableNfc();
288 if (result == DriverResult::Success) { 289 if (result == DriverResult::Success) {
289 result = nfc_protocol->StartNFCPollingMode();
290 }
291 if (result == DriverResult::Success) {
292 disable_input_thread = false;
293 return result; 290 return result;
294 } 291 }
295 nfc_protocol->DisableNfc(); 292 nfc_protocol->DisableNfc();
@@ -303,7 +300,6 @@ DriverResult JoyconDriver::SetPollingMode() {
303 } 300 }
304 if (result == DriverResult::Success) { 301 if (result == DriverResult::Success) {
305 ring_connected = true; 302 ring_connected = true;
306 disable_input_thread = false;
307 return result; 303 return result;
308 } 304 }
309 ring_connected = false; 305 ring_connected = false;
@@ -314,7 +310,6 @@ DriverResult JoyconDriver::SetPollingMode() {
314 if (passive_enabled && supported_features.passive) { 310 if (passive_enabled && supported_features.passive) {
315 const auto result = generic_protocol->EnablePassiveMode(); 311 const auto result = generic_protocol->EnablePassiveMode();
316 if (result == DriverResult::Success) { 312 if (result == DriverResult::Success) {
317 disable_input_thread = false;
318 return result; 313 return result;
319 } 314 }
320 LOG_ERROR(Input, "Error enabling passive mode"); 315 LOG_ERROR(Input, "Error enabling passive mode");
@@ -328,7 +323,6 @@ DriverResult JoyconDriver::SetPollingMode() {
328 // Switch calls this function after enabling active mode 323 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed(); 324 generic_protocol->TriggersElapsed();
330 325
331 disable_input_thread = false;
332 return result; 326 return result;
333} 327}
334 328
@@ -492,9 +486,63 @@ DriverResult JoyconDriver::SetRingConMode() {
492 return result; 486 return result;
493} 487}
494 488
495DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { 489DriverResult JoyconDriver::StartNfcPolling() {
496 std::scoped_lock lock{mutex}; 490 std::scoped_lock lock{mutex};
491
492 if (!supported_features.nfc) {
493 return DriverResult::NotSupported;
494 }
495 if (!nfc_protocol->IsEnabled()) {
496 return DriverResult::Disabled;
497 }
498
499 disable_input_thread = true;
500 const auto result = nfc_protocol->StartNFCPollingMode();
501 disable_input_thread = false;
502
503 return result;
504}
505
506DriverResult JoyconDriver::StopNfcPolling() {
507 std::scoped_lock lock{mutex};
508
509 if (!supported_features.nfc) {
510 return DriverResult::NotSupported;
511 }
512 if (!nfc_protocol->IsEnabled()) {
513 return DriverResult::Disabled;
514 }
515
516 disable_input_thread = true;
517 const auto result = nfc_protocol->StopNFCPollingMode();
518 disable_input_thread = false;
519
520 return result;
521}
522
523DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
524 std::scoped_lock lock{mutex};
525
526 if (!supported_features.nfc) {
527 return DriverResult::NotSupported;
528 }
529 if (!nfc_protocol->IsEnabled()) {
530 return DriverResult::Disabled;
531 }
532 if (!amiibo_detected) {
533 return DriverResult::ErrorWritingData;
534 }
535
536 out_data.resize(0x21C);
497 disable_input_thread = true; 537 disable_input_thread = true;
538 const auto result = nfc_protocol->ReadAmiibo(out_data);
539 disable_input_thread = false;
540
541 return result;
542}
543
544DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
545 std::scoped_lock lock{mutex};
498 546
499 if (!supported_features.nfc) { 547 if (!supported_features.nfc) {
500 return DriverResult::NotSupported; 548 return DriverResult::NotSupported;
@@ -506,9 +554,51 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
506 return DriverResult::ErrorWritingData; 554 return DriverResult::ErrorWritingData;
507 } 555 }
508 556
557 disable_input_thread = true;
509 const auto result = nfc_protocol->WriteAmiibo(data); 558 const auto result = nfc_protocol->WriteAmiibo(data);
559 disable_input_thread = false;
510 560
561 return result;
562}
563
564DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
565 std::span<MifareReadData> out_data) {
566 std::scoped_lock lock{mutex};
567
568 if (!supported_features.nfc) {
569 return DriverResult::NotSupported;
570 }
571 if (!nfc_protocol->IsEnabled()) {
572 return DriverResult::Disabled;
573 }
574 if (!amiibo_detected) {
575 return DriverResult::ErrorWritingData;
576 }
577
578 disable_input_thread = true;
579 const auto result = nfc_protocol->ReadMifare(data, out_data);
580 disable_input_thread = false;
581
582 return result;
583}
584
585DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
586 std::scoped_lock lock{mutex};
587
588 if (!supported_features.nfc) {
589 return DriverResult::NotSupported;
590 }
591 if (!nfc_protocol->IsEnabled()) {
592 return DriverResult::Disabled;
593 }
594 if (!amiibo_detected) {
595 return DriverResult::ErrorWritingData;
596 }
597
598 disable_input_thread = true;
599 const auto result = nfc_protocol->WriteMifare(data);
511 disable_input_thread = false; 600 disable_input_thread = false;
601
512 return result; 602 return result;
513} 603}
514 604
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
index e9b2fccbb..bc7025a21 100644
--- a/src/input_common/helpers/joycon_driver.h
+++ b/src/input_common/helpers/joycon_driver.h
@@ -49,7 +49,13 @@ public:
49 DriverResult SetIrMode(); 49 DriverResult SetIrMode();
50 DriverResult SetNfcMode(); 50 DriverResult SetNfcMode();
51 DriverResult SetRingConMode(); 51 DriverResult SetRingConMode();
52 DriverResult StartNfcPolling();
53 DriverResult StopNfcPolling();
54 DriverResult ReadAmiiboData(std::vector<u8>& out_data);
52 DriverResult WriteNfcData(std::span<const u8> data); 55 DriverResult WriteNfcData(std::span<const u8> data);
56 DriverResult ReadMifareData(std::span<const MifareReadChunk> request,
57 std::span<MifareReadData> out_data);
58 DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);
53 59
54 void SetCallbacks(const JoyconCallbacks& callbacks); 60 void SetCallbacks(const JoyconCallbacks& callbacks);
55 61
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 5007b0e18..e0e431156 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -24,6 +24,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x
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>; 26using TagUUID = std::array<u8, 7>;
27using MifareUUID = std::array<u8, 4>;
27 28
28enum class ControllerType : u8 { 29enum class ControllerType : u8 {
29 None = 0x00, 30 None = 0x00,
@@ -307,6 +308,19 @@ enum class NFCStatus : u8 {
307 WriteDone = 0x05, 308 WriteDone = 0x05,
308 TagLost = 0x07, 309 TagLost = 0x07,
309 WriteReady = 0x09, 310 WriteReady = 0x09,
311 MifareDone = 0x10,
312};
313
314enum class MifareCmd : u8 {
315 None = 0x00,
316 Read = 0x30,
317 AuthA = 0x60,
318 AuthB = 0x61,
319 Write = 0xA0,
320 Transfer = 0xB0,
321 Decrement = 0xC0,
322 Increment = 0xC1,
323 Store = 0xC2
310}; 324};
311 325
312enum class IrsMode : u8 { 326enum class IrsMode : u8 {
@@ -592,6 +606,14 @@ struct NFCWriteCommandData {
592static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size"); 606static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
593#pragma pack(pop) 607#pragma pack(pop)
594 608
609struct MifareCommandData {
610 u8 unknown1;
611 u8 unknown2;
612 u8 number_of_short_bytes;
613 MifareUUID uid;
614};
615static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size");
616
595struct NFCPollingCommandData { 617struct NFCPollingCommandData {
596 u8 enable_mifare; 618 u8 enable_mifare;
597 u8 unknown_1; 619 u8 unknown_1;
@@ -629,6 +651,41 @@ struct NFCWritePackage {
629 std::array<NFCDataChunk, 4> data_chunks; 651 std::array<NFCDataChunk, 4> data_chunks;
630}; 652};
631 653
654struct MifareReadChunk {
655 MifareCmd command;
656 std::array<u8, 0x6> sector_key;
657 u8 sector;
658};
659
660struct MifareWriteChunk {
661 MifareCmd command;
662 std::array<u8, 0x6> sector_key;
663 u8 sector;
664 std::array<u8, 0x10> data;
665};
666
667struct MifareReadData {
668 u8 sector;
669 std::array<u8, 0x10> data;
670};
671
672struct MifareReadPackage {
673 MifareCommandData command_data;
674 std::array<MifareReadChunk, 0x10> data_chunks;
675};
676
677struct MifareWritePackage {
678 MifareCommandData command_data;
679 std::array<MifareWriteChunk, 0x10> data_chunks;
680};
681
682struct TagInfo {
683 u8 uuid_length;
684 u8 protocol;
685 u8 tag_type;
686 std::array<u8, 10> uuid;
687};
688
632struct IrsConfigure { 689struct IrsConfigure {
633 MCUCommand command; 690 MCUCommand command;
634 MCUSubCommand sub_command; 691 MCUSubCommand sub_command;
@@ -744,7 +801,7 @@ struct JoyconCallbacks {
744 std::function<void(int, f32)> on_stick_data; 801 std::function<void(int, f32)> on_stick_data;
745 std::function<void(int, const MotionData&)> on_motion_data; 802 std::function<void(int, const MotionData&)> on_motion_data;
746 std::function<void(f32)> on_ring_data; 803 std::function<void(f32)> on_ring_data;
747 std::function<void(const std::vector<u8>&)> on_amiibo_data; 804 std::function<void(const Joycon::TagInfo&)> on_amiibo_data;
748 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data; 805 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
749}; 806};
750 807
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
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index eb58c427d..0be95e40e 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -25,14 +25,25 @@ public:
25 25
26 DriverResult StartNFCPollingMode(); 26 DriverResult StartNFCPollingMode();
27 27
28 DriverResult ScanAmiibo(std::vector<u8>& data); 28 DriverResult StopNFCPollingMode();
29
30 DriverResult GetTagInfo(Joycon::TagInfo& tag_info);
31
32 DriverResult ReadAmiibo(std::vector<u8>& data);
29 33
30 DriverResult WriteAmiibo(std::span<const u8> data); 34 DriverResult WriteAmiibo(std::span<const u8> data);
31 35
36 DriverResult ReadMifare(std::span<const MifareReadChunk> read_request,
37 std::span<MifareReadData> out_data);
38
39 DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request);
40
32 bool HasAmiibo(); 41 bool HasAmiibo();
33 42
34 bool IsEnabled() const; 43 bool IsEnabled() const;
35 44
45 bool IsPolling() const;
46
36private: 47private:
37 // Number of times the function will be delayed until it outputs valid data 48 // Number of times the function will be delayed until it outputs valid data
38 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15; 49 static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15;
@@ -51,6 +62,13 @@ private:
51 62
52 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); 63 DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
53 64
65 DriverResult GetMifareData(const MifareUUID& tag_uuid,
66 std::span<const MifareReadChunk> read_request,
67 std::span<MifareReadData> out_data);
68
69 DriverResult WriteMifareData(const MifareUUID& tag_uuid,
70 std::span<const MifareWriteChunk> write_request);
71
54 DriverResult SendStartPollingRequest(MCUCommandResponse& output, 72 DriverResult SendStartPollingRequest(MCUCommandResponse& output,
55 bool is_second_attempt = false); 73 bool is_second_attempt = false);
56 74
@@ -65,17 +83,31 @@ private:
65 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, 83 DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
66 bool is_last_packet, std::span<const u8> data); 84 bool is_last_packet, std::span<const u8> data);
67 85
86 DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
87 bool is_last_packet, std::span<const u8> data);
88
68 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; 89 std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
69 90
91 std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const;
92
93 std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const;
94
70 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const; 95 NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;
71 96
72 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; 97 NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const;
73 98
99 MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid,
100 std::span<const MifareReadChunk> read_request) const;
101
102 MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid,
103 std::span<const MifareWriteChunk> read_request) const;
104
74 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; 105 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
75 106
76 TagUUID GetTagUUID(std::span<const u8> data) const; 107 TagUUID GetTagUUID(std::span<const u8> data) const;
77 108
78 bool is_enabled{}; 109 bool is_enabled{};
110 bool is_polling{};
79 std::size_t update_counter{}; 111 std::size_t update_counter{};
80}; 112};
81 113
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
index dca797f7a..1aab9e12a 100644
--- a/src/input_common/helpers/joycon_protocol/poller.cpp
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -70,8 +70,8 @@ void JoyconPoller::UpdateColor(const Color& color) {
70 callbacks.on_color_data(color); 70 callbacks.on_color_data(color);
71} 71}
72 72
73void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { 73void JoyconPoller::UpdateAmiibo(const Joycon::TagInfo& tag_info) {
74 callbacks.on_amiibo_data(amiibo_data); 74 callbacks.on_amiibo_data(tag_info);
75} 75}
76 76
77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { 77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h
index 0fa72c6db..3746abe5d 100644
--- a/src/input_common/helpers/joycon_protocol/poller.h
+++ b/src/input_common/helpers/joycon_protocol/poller.h
@@ -36,8 +36,8 @@ public:
36 36
37 void UpdateColor(const Color& color); 37 void UpdateColor(const Color& color);
38 void UpdateRing(s16 value, const RingStatus& ring_status); 38 void UpdateRing(s16 value, const RingStatus& ring_status);
39 void UpdateAmiibo(const std::vector<u8>& amiibo_data); 39 void UpdateAmiibo(const Joycon::TagInfo& tag_info);
40 void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); 40 void UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format);
41 41
42private: 42private:
43 void UpdateActiveLeftPadInput(const InputReportActive& input, 43 void UpdateActiveLeftPadInput(const InputReportActive& input,
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 50b5a3dc8..c2d0cbb34 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -143,12 +143,46 @@ public:
143 return Common::Input::NfcState::NotSupported; 143 return Common::Input::NfcState::NotSupported;
144 } 144 }
145 145
146 // Start scanning for nfc tags
147 virtual Common::Input::NfcState StartNfcPolling(
148 [[maybe_unused]] const PadIdentifier& identifier_) {
149 return Common::Input::NfcState::NotSupported;
150 }
151
152 // Start scanning for nfc tags
153 virtual Common::Input::NfcState StopNfcPolling(
154 [[maybe_unused]] const PadIdentifier& identifier_) {
155 return Common::Input::NfcState::NotSupported;
156 }
157
158 // Reads data from amiibo tag
159 virtual Common::Input::NfcState ReadAmiiboData(
160 [[maybe_unused]] const PadIdentifier& identifier_,
161 [[maybe_unused]] std::vector<u8>& out_data) {
162 return Common::Input::NfcState::NotSupported;
163 }
164
146 // Writes data to an nfc tag 165 // Writes data to an nfc tag
147 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, 166 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,
148 [[maybe_unused]] const std::vector<u8>& data) { 167 [[maybe_unused]] const std::vector<u8>& data) {
149 return Common::Input::NfcState::NotSupported; 168 return Common::Input::NfcState::NotSupported;
150 } 169 }
151 170
171 // Reads data from mifare tag
172 virtual Common::Input::NfcState ReadMifareData(
173 [[maybe_unused]] const PadIdentifier& identifier_,
174 [[maybe_unused]] const Common::Input::MifareRequest& request,
175 [[maybe_unused]] Common::Input::MifareRequest& out_data) {
176 return Common::Input::NfcState::NotSupported;
177 }
178
179 // Write data to mifare tag
180 virtual Common::Input::NfcState WriteMifareData(
181 [[maybe_unused]] const PadIdentifier& identifier_,
182 [[maybe_unused]] const Common::Input::MifareRequest& request) {
183 return Common::Input::NfcState::NotSupported;
184 }
185
152 // Returns the engine name 186 // Returns the engine name
153 [[nodiscard]] const std::string& GetEngineName() const; 187 [[nodiscard]] const std::string& GetEngineName() const;
154 188
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 380a01542..870e76ab0 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -792,8 +792,7 @@ public:
792 792
793 const Common::Input::CallbackStatus status{ 793 const Common::Input::CallbackStatus status{
794 .type = Common::Input::InputType::Nfc, 794 .type = Common::Input::InputType::Nfc,
795 .nfc_status = nfc_status.state, 795 .nfc_status = nfc_status,
796 .raw_data = nfc_status.data,
797 }; 796 };
798 797
799 TriggerOnChange(status); 798 TriggerOnChange(status);
@@ -836,10 +835,31 @@ public:
836 return input_engine->SupportsNfc(identifier); 835 return input_engine->SupportsNfc(identifier);
837 } 836 }
838 837
838 Common::Input::NfcState StartNfcPolling() {
839 return input_engine->StartNfcPolling(identifier);
840 }
841
842 Common::Input::NfcState StopNfcPolling() {
843 return input_engine->StopNfcPolling(identifier);
844 }
845
846 Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) {
847 return input_engine->ReadAmiiboData(identifier, out_data);
848 }
849
839 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override { 850 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {
840 return input_engine->WriteNfcData(identifier, data); 851 return input_engine->WriteNfcData(identifier, data);
841 } 852 }
842 853
854 Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
855 Common::Input::MifareRequest& out_data) {
856 return input_engine->ReadMifareData(identifier, request, out_data);
857 }
858
859 Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) {
860 return input_engine->WriteMifareData(identifier, request);
861 }
862
843private: 863private:
844 const PadIdentifier identifier; 864 const PadIdentifier identifier;
845 InputEngine* input_engine; 865 InputEngine* input_engine;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 013715b44..cba7c3cce 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3836,7 +3836,7 @@ void GMainWindow::OnLoadAmiibo() {
3836 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3836 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3837 3837
3838 // Remove amiibo if one is connected 3838 // Remove amiibo if one is connected
3839 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3839 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3840 virtual_amiibo->CloseAmiibo(); 3840 virtual_amiibo->CloseAmiibo();
3841 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3841 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3842 return; 3842 return;
@@ -3864,7 +3864,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
3864 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 3864 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3865 const QString title = tr("Error loading Amiibo data"); 3865 const QString title = tr("Error loading Amiibo data");
3866 // Remove amiibo if one is connected 3866 // Remove amiibo if one is connected
3867 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { 3867 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
3868 virtual_amiibo->CloseAmiibo(); 3868 virtual_amiibo->CloseAmiibo();
3869 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3869 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3870 return; 3870 return;