summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_protocol/nfc.cpp
diff options
context:
space:
mode:
authorGravatar liamwhite2023-01-24 09:29:37 -0500
committerGravatar GitHub2023-01-24 09:29:37 -0500
commita68af583ea378b48e2ed5a19f519a815ba89e40f (patch)
tree2983c14a7d4bc2797259c7d97462a439bec629f3 /src/input_common/helpers/joycon_protocol/nfc.cpp
parentMerge pull request #9555 from abouvier/catch2-update (diff)
parentcore: hid: Make use of SCOPE_EXIT and SCOPE_GUARD where applicable (diff)
downloadyuzu-a68af583ea378b48e2ed5a19f519a815ba89e40f.tar.gz
yuzu-a68af583ea378b48e2ed5a19f519a815ba89e40f.tar.xz
yuzu-a68af583ea378b48e2ed5a19f519a815ba89e40f.zip
Merge pull request #9492 from german77/joycon_release
Input_common: Implement custom joycon driver v2
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/nfc.cpp')
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp400
1 files changed, 400 insertions, 0 deletions
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
new file mode 100644
index 000000000..5c0f71722
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -0,0 +1,400 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/nfc.h"
7
8namespace InputCommon::Joycon {
9
10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult NfcProtocol::EnableNfc() {
14 LOG_INFO(Input, "Enable NFC");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::NFC,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37
38 return result;
39}
40
41DriverResult NfcProtocol::DisableNfc() {
42 LOG_DEBUG(Input, "Disable NFC");
43 ScopedSetBlocking sb(this);
44 DriverResult result{DriverResult::Success};
45
46 if (result == DriverResult::Success) {
47 result = EnableMCU(false);
48 }
49
50 is_enabled = false;
51
52 return result;
53}
54
55DriverResult NfcProtocol::StartNFCPollingMode() {
56 LOG_DEBUG(Input, "Start NFC pooling Mode");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59 TagFoundData tag_data{};
60
61 if (result == DriverResult::Success) {
62 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
63 }
64 if (result == DriverResult::Success) {
65 result = WaitUntilNfcIsReady();
66 }
67 if (result == DriverResult::Success) {
68 is_enabled = true;
69 }
70
71 return result;
72}
73
74DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
75 LOG_DEBUG(Input, "Start NFC pooling Mode");
76 ScopedSetBlocking sb(this);
77 DriverResult result{DriverResult::Success};
78 TagFoundData tag_data{};
79
80 if (result == DriverResult::Success) {
81 result = StartPolling(tag_data);
82 }
83 if (result == DriverResult::Success) {
84 result = ReadTag(tag_data);
85 }
86 if (result == DriverResult::Success) {
87 result = WaitUntilNfcIsReady();
88 }
89 if (result == DriverResult::Success) {
90 result = StartPolling(tag_data);
91 }
92 if (result == DriverResult::Success) {
93 result = GetAmiiboData(data);
94 }
95
96 return result;
97}
98
99bool NfcProtocol::HasAmiibo() {
100 ScopedSetBlocking sb(this);
101 DriverResult result{DriverResult::Success};
102 TagFoundData tag_data{};
103
104 if (result == DriverResult::Success) {
105 result = StartPolling(tag_data);
106 }
107
108 return result == DriverResult::Success;
109}
110
111DriverResult NfcProtocol::WaitUntilNfcIsReady() {
112 constexpr std::size_t timeout_limit = 10;
113 std::vector<u8> output;
114 std::size_t tries = 0;
115
116 do {
117 auto result = SendStartWaitingRecieveRequest(output);
118
119 if (result != DriverResult::Success) {
120 return result;
121 }
122 if (tries++ > timeout_limit) {
123 return DriverResult::Timeout;
124 }
125 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 ||
126 output[56] != 0x00);
127
128 return DriverResult::Success;
129}
130
131DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
132 LOG_DEBUG(Input, "Start Polling for tag");
133 constexpr std::size_t timeout_limit = 7;
134 std::vector<u8> output;
135 std::size_t tries = 0;
136
137 do {
138 const auto result = SendStartPollingRequest(output);
139 if (result != DriverResult::Success) {
140 return result;
141 }
142 if (tries++ > timeout_limit) {
143 return DriverResult::Timeout;
144 }
145 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[56] != 0x09);
146
147 data.type = output[62];
148 data.uuid.resize(output[64]);
149 memcpy(data.uuid.data(), output.data() + 65, data.uuid.size());
150
151 return DriverResult::Success;
152}
153
154DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
155 constexpr std::size_t timeout_limit = 10;
156 std::vector<u8> output;
157 std::size_t tries = 0;
158
159 std::string uuid_string;
160 for (auto& content : data.uuid) {
161 uuid_string += fmt::format(" {:02x}", content);
162 }
163
164 LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
165
166 tries = 0;
167 NFCPages ntag_pages = NFCPages::Block0;
168 // Read Tag data
169 while (true) {
170 auto result = SendReadAmiiboRequest(output, ntag_pages);
171 const auto mcu_report = static_cast<MCUReport>(output[49]);
172 const auto nfc_status = static_cast<NFCStatus>(output[56]);
173
174 if (result != DriverResult::Success) {
175 return result;
176 }
177
178 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) &&
179 nfc_status == NFCStatus::TagLost) {
180 return DriverResult::ErrorReadingData;
181 }
182
183 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07 && output[52] == 0x01) {
184 if (data.type != 2) {
185 continue;
186 }
187 switch (output[74]) {
188 case 0:
189 ntag_pages = NFCPages::Block135;
190 break;
191 case 3:
192 ntag_pages = NFCPages::Block45;
193 break;
194 case 4:
195 ntag_pages = NFCPages::Block231;
196 break;
197 default:
198 return DriverResult::ErrorReadingData;
199 }
200 continue;
201 }
202
203 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
204 // finished
205 SendStopPollingRequest(output);
206 return DriverResult::Success;
207 }
208
209 // Ignore other state reports
210 if (mcu_report == MCUReport::NFCState) {
211 continue;
212 }
213
214 if (tries++ > timeout_limit) {
215 return DriverResult::Timeout;
216 }
217 }
218
219 return DriverResult::Success;
220}
221
222DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
223 constexpr std::size_t timeout_limit = 10;
224 std::vector<u8> output;
225 std::size_t tries = 0;
226
227 NFCPages ntag_pages = NFCPages::Block135;
228 std::size_t ntag_buffer_pos = 0;
229 // Read Tag data
230 while (true) {
231 auto result = SendReadAmiiboRequest(output, ntag_pages);
232 const auto mcu_report = static_cast<MCUReport>(output[49]);
233 const auto nfc_status = static_cast<NFCStatus>(output[56]);
234
235 if (result != DriverResult::Success) {
236 return result;
237 }
238
239 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) &&
240 nfc_status == NFCStatus::TagLost) {
241 return DriverResult::ErrorReadingData;
242 }
243
244 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) {
245 std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF;
246 if (output[52] == 0x01) {
247 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60);
248 ntag_buffer_pos += payload_size - 60;
249 } else {
250 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size);
251 }
252 continue;
253 }
254
255 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
256 LOG_INFO(Input, "Finished reading amiibo");
257 return DriverResult::Success;
258 }
259
260 // Ignore other state reports
261 if (mcu_report == MCUReport::NFCState) {
262 continue;
263 }
264
265 if (tries++ > timeout_limit) {
266 return DriverResult::Timeout;
267 }
268 }
269
270 return DriverResult::Success;
271}
272
273DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) {
274 NFCRequestState request{
275 .sub_command = MCUSubCommand::ReadDeviceMode,
276 .command_argument = NFCReadCommand::StartPolling,
277 .packet_id = 0x0,
278 .packet_flag = MCUPacketFlag::LastCommandPacket,
279 .data_length = sizeof(NFCPollingCommandData),
280 .nfc_polling =
281 {
282 .enable_mifare = 0x01,
283 .unknown_1 = 0x00,
284 .unknown_2 = 0x00,
285 .unknown_3 = 0x2c,
286 .unknown_4 = 0x01,
287 },
288 .crc = {},
289 };
290
291 std::array<u8, sizeof(NFCRequestState)> request_data{};
292 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
293 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
294 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
295}
296
297DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) {
298 NFCRequestState request{
299 .sub_command = MCUSubCommand::ReadDeviceMode,
300 .command_argument = NFCReadCommand::StopPolling,
301 .packet_id = 0x0,
302 .packet_flag = MCUPacketFlag::LastCommandPacket,
303 .data_length = 0,
304 .raw_data = {},
305 .crc = {},
306 };
307
308 std::array<u8, sizeof(NFCRequestState)> request_data{};
309 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
310 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
311 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
312}
313
314DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output) {
315 NFCRequestState request{
316 .sub_command = MCUSubCommand::ReadDeviceMode,
317 .command_argument = NFCReadCommand::StartWaitingRecieve,
318 .packet_id = 0x0,
319 .packet_flag = MCUPacketFlag::LastCommandPacket,
320 .data_length = 0,
321 .raw_data = {},
322 .crc = {},
323 };
324
325 std::vector<u8> request_data(sizeof(NFCRequestState));
326 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
327 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
328 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
329}
330
331DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages) {
332 NFCRequestState request{
333 .sub_command = MCUSubCommand::ReadDeviceMode,
334 .command_argument = NFCReadCommand::Ntag,
335 .packet_id = 0x0,
336 .packet_flag = MCUPacketFlag::LastCommandPacket,
337 .data_length = sizeof(NFCReadCommandData),
338 .nfc_read =
339 {
340 .unknown = 0xd0,
341 .uuid_length = 0x07,
342 .unknown_2 = 0x00,
343 .uid = {},
344 .tag_type = NFCTagType::AllTags,
345 .read_block = GetReadBlockCommand(ntag_pages),
346 },
347 .crc = {},
348 };
349
350 std::array<u8, sizeof(NFCRequestState)> request_data{};
351 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
352 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
353 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
354}
355
356NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
357 switch (pages) {
358 case NFCPages::Block0:
359 return {
360 .block_count = 1,
361 };
362 case NFCPages::Block45:
363 return {
364 .block_count = 1,
365 .blocks =
366 {
367 NFCReadBlock{0x00, 0x2C},
368 },
369 };
370 case NFCPages::Block135:
371 return {
372 .block_count = 3,
373 .blocks =
374 {
375 NFCReadBlock{0x00, 0x3b},
376 {0x3c, 0x77},
377 {0x78, 0x86},
378 },
379 };
380 case NFCPages::Block231:
381 return {
382 .block_count = 4,
383 .blocks =
384 {
385 NFCReadBlock{0x00, 0x3b},
386 {0x3c, 0x77},
387 {0x78, 0x83},
388 {0xb4, 0xe6},
389 },
390 };
391 default:
392 return {};
393 };
394}
395
396bool NfcProtocol::IsEnabled() const {
397 return is_enabled;
398}
399
400} // namespace InputCommon::Joycon