diff options
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/service/nfc/common/device.cpp | 8 | ||||
| -rw-r--r-- | src/input_common/drivers/joycon.cpp | 8 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.cpp | 20 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.h | 1 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/joycon_types.h | 50 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/nfc.cpp | 332 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/nfc.h | 27 |
8 files changed, 387 insertions, 68 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 366880711..bbfea7117 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -1283,9 +1283,14 @@ bool EmulatedController::HasNfc() const { | |||
| 1283 | } | 1283 | } |
| 1284 | 1284 | ||
| 1285 | bool EmulatedController::WriteNfc(const std::vector<u8>& data) { | 1285 | bool EmulatedController::WriteNfc(const std::vector<u8>& data) { |
| 1286 | auto& nfc_output_device = output_devices[3]; | 1286 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1287 | auto& nfc_virtual_output_device = output_devices[3]; | ||
| 1288 | |||
| 1289 | if (nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported) { | ||
| 1290 | return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; | ||
| 1291 | } | ||
| 1287 | 1292 | ||
| 1288 | return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; | 1293 | return nfc_virtual_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; |
| 1289 | } | 1294 | } |
| 1290 | 1295 | ||
| 1291 | void EmulatedController::SetLedPattern() { | 1296 | void EmulatedController::SetLedPattern() { |
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 322bde2ed..8a7e9edac 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp | |||
| @@ -421,11 +421,11 @@ Result NfcDevice::Flush() { | |||
| 421 | 421 | ||
| 422 | tag_data.write_counter++; | 422 | tag_data.write_counter++; |
| 423 | 423 | ||
| 424 | FlushWithBreak(NFP::BreakType::Normal); | 424 | const auto result = FlushWithBreak(NFP::BreakType::Normal); |
| 425 | 425 | ||
| 426 | is_data_moddified = false; | 426 | is_data_moddified = false; |
| 427 | 427 | ||
| 428 | return ResultSuccess; | 428 | return result; |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | Result NfcDevice::FlushDebug() { | 431 | Result NfcDevice::FlushDebug() { |
| @@ -444,11 +444,11 @@ Result NfcDevice::FlushDebug() { | |||
| 444 | 444 | ||
| 445 | tag_data.write_counter++; | 445 | tag_data.write_counter++; |
| 446 | 446 | ||
| 447 | FlushWithBreak(NFP::BreakType::Normal); | 447 | const auto result = FlushWithBreak(NFP::BreakType::Normal); |
| 448 | 448 | ||
| 449 | is_data_moddified = false; | 449 | is_data_moddified = false; |
| 450 | 450 | ||
| 451 | return ResultSuccess; | 451 | return result; |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { | 454 | Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { |
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 653862a72..b2b5677c8 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp | |||
| @@ -291,9 +291,13 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c | |||
| 291 | return Common::Input::NfcState::Success; | 291 | return Common::Input::NfcState::Success; |
| 292 | }; | 292 | }; |
| 293 | 293 | ||
| 294 | Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_, | 294 | Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier, |
| 295 | const std::vector<u8>& data) { | 295 | const std::vector<u8>& data) { |
| 296 | return Common::Input::NfcState::NotSupported; | 296 | auto handle = GetHandle(identifier); |
| 297 | if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { | ||
| 298 | return Common::Input::NfcState::WriteFailed; | ||
| 299 | } | ||
| 300 | return Common::Input::NfcState::Success; | ||
| 297 | }; | 301 | }; |
| 298 | 302 | ||
| 299 | Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, | 303 | Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, |
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 83429a336..95106f16d 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp | |||
| @@ -492,6 +492,26 @@ DriverResult JoyconDriver::SetRingConMode() { | |||
| 492 | return result; | 492 | return result; |
| 493 | } | 493 | } |
| 494 | 494 | ||
| 495 | DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { | ||
| 496 | std::scoped_lock lock{mutex}; | ||
| 497 | disable_input_thread = true; | ||
| 498 | |||
| 499 | if (!supported_features.nfc) { | ||
| 500 | return DriverResult::NotSupported; | ||
| 501 | } | ||
| 502 | if (!nfc_protocol->IsEnabled()) { | ||
| 503 | return DriverResult::Disabled; | ||
| 504 | } | ||
| 505 | if (!amiibo_detected) { | ||
| 506 | return DriverResult::ErrorWritingData; | ||
| 507 | } | ||
| 508 | |||
| 509 | const auto result = nfc_protocol->WriteAmiibo(data); | ||
| 510 | |||
| 511 | disable_input_thread = false; | ||
| 512 | return result; | ||
| 513 | } | ||
| 514 | |||
| 495 | bool JoyconDriver::IsConnected() const { | 515 | bool JoyconDriver::IsConnected() const { |
| 496 | std::scoped_lock lock{mutex}; | 516 | std::scoped_lock lock{mutex}; |
| 497 | return is_connected.load(); | 517 | return is_connected.load(); |
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index 72a9e71dc..e9b2fccbb 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h | |||
| @@ -49,6 +49,7 @@ public: | |||
| 49 | DriverResult SetIrMode(); | 49 | DriverResult SetIrMode(); |
| 50 | DriverResult SetNfcMode(); | 50 | DriverResult SetNfcMode(); |
| 51 | DriverResult SetRingConMode(); | 51 | DriverResult SetRingConMode(); |
| 52 | DriverResult WriteNfcData(std::span<const u8> data); | ||
| 52 | 53 | ||
| 53 | void SetCallbacks(const JoyconCallbacks& callbacks); | 54 | void SetCallbacks(const JoyconCallbacks& callbacks); |
| 54 | 55 | ||
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 353dc744d..5007b0e18 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h | |||
| @@ -23,6 +23,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x | |||
| 23 | 23 | ||
| 24 | using MacAddress = std::array<u8, 6>; | 24 | using MacAddress = std::array<u8, 6>; |
| 25 | using SerialNumber = std::array<u8, 15>; | 25 | using SerialNumber = std::array<u8, 15>; |
| 26 | using TagUUID = std::array<u8, 7>; | ||
| 26 | 27 | ||
| 27 | enum class ControllerType : u8 { | 28 | enum class ControllerType : u8 { |
| 28 | None = 0x00, | 29 | None = 0x00, |
| @@ -276,12 +277,13 @@ enum class MCUPacketFlag : u8 { | |||
| 276 | LastCommandPacket = 0x08, | 277 | LastCommandPacket = 0x08, |
| 277 | }; | 278 | }; |
| 278 | 279 | ||
| 279 | enum class NFCReadCommand : u8 { | 280 | enum class NFCCommand : u8 { |
| 280 | CancelAll = 0x00, | 281 | CancelAll = 0x00, |
| 281 | StartPolling = 0x01, | 282 | StartPolling = 0x01, |
| 282 | StopPolling = 0x02, | 283 | StopPolling = 0x02, |
| 283 | StartWaitingRecieve = 0x04, | 284 | StartWaitingRecieve = 0x04, |
| 284 | Ntag = 0x06, | 285 | ReadNtag = 0x06, |
| 286 | WriteNtag = 0x08, | ||
| 285 | Mifare = 0x0F, | 287 | Mifare = 0x0F, |
| 286 | }; | 288 | }; |
| 287 | 289 | ||
| @@ -292,14 +294,19 @@ enum class NFCTagType : u8 { | |||
| 292 | 294 | ||
| 293 | enum class NFCPages { | 295 | enum class NFCPages { |
| 294 | Block0 = 0, | 296 | Block0 = 0, |
| 297 | Block3 = 3, | ||
| 295 | Block45 = 45, | 298 | Block45 = 45, |
| 296 | Block135 = 135, | 299 | Block135 = 135, |
| 297 | Block231 = 231, | 300 | Block231 = 231, |
| 298 | }; | 301 | }; |
| 299 | 302 | ||
| 300 | enum class NFCStatus : u8 { | 303 | enum class NFCStatus : u8 { |
| 304 | Ready = 0x00, | ||
| 305 | Polling = 0x01, | ||
| 301 | LastPackage = 0x04, | 306 | LastPackage = 0x04, |
| 307 | WriteDone = 0x05, | ||
| 302 | TagLost = 0x07, | 308 | TagLost = 0x07, |
| 309 | WriteReady = 0x09, | ||
| 303 | }; | 310 | }; |
| 304 | 311 | ||
| 305 | enum class IrsMode : u8 { | 312 | enum class IrsMode : u8 { |
| @@ -559,13 +566,32 @@ static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an inv | |||
| 559 | struct NFCReadCommandData { | 566 | struct NFCReadCommandData { |
| 560 | u8 unknown; | 567 | u8 unknown; |
| 561 | u8 uuid_length; | 568 | u8 uuid_length; |
| 562 | u8 unknown_2; | 569 | TagUUID uid; |
| 563 | std::array<u8, 6> uid; | ||
| 564 | NFCTagType tag_type; | 570 | NFCTagType tag_type; |
| 565 | NFCReadBlockCommand read_block; | 571 | NFCReadBlockCommand read_block; |
| 566 | }; | 572 | }; |
| 567 | static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size"); | 573 | static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size"); |
| 568 | 574 | ||
| 575 | #pragma pack(push, 1) | ||
| 576 | struct NFCWriteCommandData { | ||
| 577 | u8 unknown; | ||
| 578 | u8 uuid_length; | ||
| 579 | TagUUID uid; | ||
| 580 | NFCTagType tag_type; | ||
| 581 | u8 unknown2; | ||
| 582 | u8 unknown3; | ||
| 583 | u8 unknown4; | ||
| 584 | u8 unknown5; | ||
| 585 | u8 unknown6; | ||
| 586 | u8 unknown7; | ||
| 587 | u8 unknown8; | ||
| 588 | u8 magic; | ||
| 589 | u16_be write_count; | ||
| 590 | u8 amiibo_version; | ||
| 591 | }; | ||
| 592 | static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size"); | ||
| 593 | #pragma pack(pop) | ||
| 594 | |||
| 569 | struct NFCPollingCommandData { | 595 | struct NFCPollingCommandData { |
| 570 | u8 enable_mifare; | 596 | u8 enable_mifare; |
| 571 | u8 unknown_1; | 597 | u8 unknown_1; |
| @@ -576,8 +602,8 @@ struct NFCPollingCommandData { | |||
| 576 | static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size"); | 602 | static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size"); |
| 577 | 603 | ||
| 578 | struct NFCRequestState { | 604 | struct NFCRequestState { |
| 579 | NFCReadCommand command_argument; | 605 | NFCCommand command_argument; |
| 580 | INSERT_PADDING_BYTES(0x1); | 606 | u8 block_id; |
| 581 | u8 packet_id; | 607 | u8 packet_id; |
| 582 | MCUPacketFlag packet_flag; | 608 | MCUPacketFlag packet_flag; |
| 583 | u8 data_length; | 609 | u8 data_length; |
| @@ -591,6 +617,18 @@ struct NFCRequestState { | |||
| 591 | }; | 617 | }; |
| 592 | static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); | 618 | static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); |
| 593 | 619 | ||
| 620 | struct NFCDataChunk { | ||
| 621 | u8 nfc_page; | ||
| 622 | u8 data_size; | ||
| 623 | std::array<u8, 0xFF> data; | ||
| 624 | }; | ||
| 625 | |||
| 626 | struct NFCWritePackage { | ||
| 627 | NFCWriteCommandData command_data; | ||
| 628 | u8 number_of_chunks; | ||
| 629 | std::array<NFCDataChunk, 4> data_chunks; | ||
| 630 | }; | ||
| 631 | |||
| 594 | struct IrsConfigure { | 632 | struct IrsConfigure { |
| 595 | MCUCommand command; | 633 | MCUCommand command; |
| 596 | MCUSubCommand sub_command; | 634 | MCUSubCommand sub_command; |
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index 46c9e9489..3b7a628e5 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp | |||
| @@ -34,6 +34,12 @@ DriverResult NfcProtocol::EnableNfc() { | |||
| 34 | 34 | ||
| 35 | result = ConfigureMCU(config); | 35 | result = ConfigureMCU(config); |
| 36 | } | 36 | } |
| 37 | if (result == DriverResult::Success) { | ||
| 38 | result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); | ||
| 39 | } | ||
| 40 | if (result == DriverResult::Success) { | ||
| 41 | result = WaitUntilNfcIs(NFCStatus::Ready); | ||
| 42 | } | ||
| 37 | 43 | ||
| 38 | return result; | 44 | return result; |
| 39 | } | 45 | } |
| @@ -56,27 +62,20 @@ DriverResult NfcProtocol::StartNFCPollingMode() { | |||
| 56 | LOG_DEBUG(Input, "Start NFC pooling Mode"); | 62 | LOG_DEBUG(Input, "Start NFC pooling Mode"); |
| 57 | ScopedSetBlocking sb(this); | 63 | ScopedSetBlocking sb(this); |
| 58 | DriverResult result{DriverResult::Success}; | 64 | DriverResult result{DriverResult::Success}; |
| 59 | TagFoundData tag_data{}; | ||
| 60 | 65 | ||
| 61 | if (result == DriverResult::Success) { | 66 | if (result == DriverResult::Success) { |
| 62 | result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); | ||
| 63 | } | ||
| 64 | if (result == DriverResult::Success) { | ||
| 65 | result = WaitUntilNfcIsReady(); | ||
| 66 | } | ||
| 67 | if (result == DriverResult::Success) { | ||
| 68 | MCUCommandResponse output{}; | 67 | MCUCommandResponse output{}; |
| 69 | result = SendStopPollingRequest(output); | 68 | result = SendStopPollingRequest(output); |
| 70 | } | 69 | } |
| 71 | if (result == DriverResult::Success) { | 70 | if (result == DriverResult::Success) { |
| 72 | result = WaitUntilNfcIsReady(); | 71 | result = WaitUntilNfcIs(NFCStatus::Ready); |
| 73 | } | 72 | } |
| 74 | if (result == DriverResult::Success) { | 73 | if (result == DriverResult::Success) { |
| 75 | MCUCommandResponse output{}; | 74 | MCUCommandResponse output{}; |
| 76 | result = SendStartPollingRequest(output); | 75 | result = SendStartPollingRequest(output); |
| 77 | } | 76 | } |
| 78 | if (result == DriverResult::Success) { | 77 | if (result == DriverResult::Success) { |
| 79 | result = WaitUntilNfcIsPolling(); | 78 | result = WaitUntilNfcIs(NFCStatus::Polling); |
| 80 | } | 79 | } |
| 81 | if (result == DriverResult::Success) { | 80 | if (result == DriverResult::Success) { |
| 82 | is_enabled = true; | 81 | is_enabled = true; |
| @@ -112,6 +111,49 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { | |||
| 112 | return result; | 111 | return result; |
| 113 | } | 112 | } |
| 114 | 113 | ||
| 114 | DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) { | ||
| 115 | LOG_DEBUG(Input, "Write amiibo"); | ||
| 116 | ScopedSetBlocking sb(this); | ||
| 117 | DriverResult result{DriverResult::Success}; | ||
| 118 | TagUUID tag_uuid = GetTagUUID(data); | ||
| 119 | TagFoundData tag_data{}; | ||
| 120 | |||
| 121 | if (result == DriverResult::Success) { | ||
| 122 | result = IsTagInRange(tag_data, 7); | ||
| 123 | } | ||
| 124 | if (result == DriverResult::Success) { | ||
| 125 | if (tag_data.uuid != tag_uuid) { | ||
| 126 | result = DriverResult::InvalidParameters; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | if (result == DriverResult::Success) { | ||
| 130 | MCUCommandResponse output{}; | ||
| 131 | result = SendStopPollingRequest(output); | ||
| 132 | } | ||
| 133 | if (result == DriverResult::Success) { | ||
| 134 | result = WaitUntilNfcIs(NFCStatus::Ready); | ||
| 135 | } | ||
| 136 | if (result == DriverResult::Success) { | ||
| 137 | MCUCommandResponse output{}; | ||
| 138 | result = SendStartPollingRequest(output, true); | ||
| 139 | } | ||
| 140 | if (result == DriverResult::Success) { | ||
| 141 | result = WaitUntilNfcIs(NFCStatus::WriteReady); | ||
| 142 | } | ||
| 143 | if (result == DriverResult::Success) { | ||
| 144 | result = WriteAmiiboData(tag_uuid, data); | ||
| 145 | } | ||
| 146 | if (result == DriverResult::Success) { | ||
| 147 | result = WaitUntilNfcIs(NFCStatus::WriteDone); | ||
| 148 | } | ||
| 149 | if (result == DriverResult::Success) { | ||
| 150 | MCUCommandResponse output{}; | ||
| 151 | result = SendStopPollingRequest(output); | ||
| 152 | } | ||
| 153 | |||
| 154 | return result; | ||
| 155 | } | ||
| 156 | |||
| 115 | bool NfcProtocol::HasAmiibo() { | 157 | bool NfcProtocol::HasAmiibo() { |
| 116 | if (update_counter++ < AMIIBO_UPDATE_DELAY) { | 158 | if (update_counter++ < AMIIBO_UPDATE_DELAY) { |
| 117 | return true; | 159 | return true; |
| @@ -129,7 +171,7 @@ bool NfcProtocol::HasAmiibo() { | |||
| 129 | return result == DriverResult::Success; | 171 | return result == DriverResult::Success; |
| 130 | } | 172 | } |
| 131 | 173 | ||
| 132 | DriverResult NfcProtocol::WaitUntilNfcIsReady() { | 174 | DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) { |
| 133 | constexpr std::size_t timeout_limit = 10; | 175 | constexpr std::size_t timeout_limit = 10; |
| 134 | MCUCommandResponse output{}; | 176 | MCUCommandResponse output{}; |
| 135 | std::size_t tries = 0; | 177 | std::size_t tries = 0; |
| @@ -145,28 +187,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() { | |||
| 145 | } | 187 | } |
| 146 | } while (output.mcu_report != MCUReport::NFCState || | 188 | } while (output.mcu_report != MCUReport::NFCState || |
| 147 | (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || | 189 | (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || |
| 148 | output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00); | 190 | output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status)); |
| 149 | |||
| 150 | return DriverResult::Success; | ||
| 151 | } | ||
| 152 | |||
| 153 | DriverResult NfcProtocol::WaitUntilNfcIsPolling() { | ||
| 154 | constexpr std::size_t timeout_limit = 10; | ||
| 155 | MCUCommandResponse output{}; | ||
| 156 | std::size_t tries = 0; | ||
| 157 | |||
| 158 | do { | ||
| 159 | auto result = SendNextPackageRequest(output, {}); | ||
| 160 | |||
| 161 | if (result != DriverResult::Success) { | ||
| 162 | return result; | ||
| 163 | } | ||
| 164 | if (tries++ > timeout_limit) { | ||
| 165 | return DriverResult::Timeout; | ||
| 166 | } | ||
| 167 | } while (output.mcu_report != MCUReport::NFCState || | ||
| 168 | (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || | ||
| 169 | output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x01); | ||
| 170 | 191 | ||
| 171 | return DriverResult::Success; | 192 | return DriverResult::Success; |
| 172 | } | 193 | } |
| @@ -188,7 +209,7 @@ DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_l | |||
| 188 | (output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04)); | 209 | (output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04)); |
| 189 | 210 | ||
| 190 | data.type = output.mcu_data[12]; | 211 | data.type = output.mcu_data[12]; |
| 191 | data.uuid.resize(output.mcu_data[14]); | 212 | data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID))); |
| 192 | memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size()); | 213 | memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size()); |
| 193 | 214 | ||
| 194 | return DriverResult::Success; | 215 | return DriverResult::Success; |
| @@ -245,17 +266,94 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { | |||
| 245 | return DriverResult::Timeout; | 266 | return DriverResult::Timeout; |
| 246 | } | 267 | } |
| 247 | 268 | ||
| 248 | DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) { | 269 | DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) { |
| 270 | constexpr std::size_t timeout_limit = 60; | ||
| 271 | const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data); | ||
| 272 | const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data); | ||
| 273 | std::span<const u8> buffer(nfc_buffer_data); | ||
| 274 | MCUCommandResponse output{}; | ||
| 275 | u8 block_id = 1; | ||
| 276 | u8 package_index = 0; | ||
| 277 | std::size_t tries = 0; | ||
| 278 | std::size_t current_position = 0; | ||
| 279 | |||
| 280 | LOG_INFO(Input, "Writing amiibo data"); | ||
| 281 | |||
| 282 | auto result = SendWriteAmiiboRequest(output, tag_uuid); | ||
| 283 | |||
| 284 | if (result != DriverResult::Success) { | ||
| 285 | return result; | ||
| 286 | } | ||
| 287 | |||
| 288 | // Read Tag data but ignore the actual sent data | ||
| 289 | while (tries++ < timeout_limit) { | ||
| 290 | result = SendNextPackageRequest(output, package_index); | ||
| 291 | const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); | ||
| 292 | |||
| 293 | if (result != DriverResult::Success) { | ||
| 294 | return result; | ||
| 295 | } | ||
| 296 | |||
| 297 | if ((output.mcu_report == MCUReport::NFCReadData || | ||
| 298 | output.mcu_report == MCUReport::NFCState) && | ||
| 299 | nfc_status == NFCStatus::TagLost) { | ||
| 300 | return DriverResult::ErrorReadingData; | ||
| 301 | } | ||
| 302 | |||
| 303 | if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { | ||
| 304 | package_index++; | ||
| 305 | continue; | ||
| 306 | } | ||
| 307 | |||
| 308 | if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { | ||
| 309 | LOG_INFO(Input, "Finished reading amiibo"); | ||
| 310 | break; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | // Send Data. Nfc buffer size is 31, Send the data in smaller packages | ||
| 315 | while (current_position < buffer.size() && tries++ < timeout_limit) { | ||
| 316 | const std::size_t next_position = | ||
| 317 | std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); | ||
| 318 | const std::size_t block_size = next_position - current_position; | ||
| 319 | const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); | ||
| 320 | |||
| 321 | SendWriteDataAmiiboRequest(output, block_id, is_last_packet, | ||
| 322 | buffer.subspan(current_position, block_size)); | ||
| 323 | |||
| 324 | const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); | ||
| 325 | |||
| 326 | if ((output.mcu_report == MCUReport::NFCReadData || | ||
| 327 | output.mcu_report == MCUReport::NFCState) && | ||
| 328 | nfc_status == NFCStatus::TagLost) { | ||
| 329 | return DriverResult::ErrorReadingData; | ||
| 330 | } | ||
| 331 | |||
| 332 | // Increase position when data is confirmed by the joycon | ||
| 333 | if (output.mcu_report == MCUReport::NFCState && | ||
| 334 | (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && | ||
| 335 | output.mcu_data[3] == block_id) { | ||
| 336 | block_id++; | ||
| 337 | current_position = next_position; | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | return result; | ||
| 342 | } | ||
| 343 | |||
| 344 | DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, | ||
| 345 | bool is_second_attempt) { | ||
| 249 | NFCRequestState request{ | 346 | NFCRequestState request{ |
| 250 | .command_argument = NFCReadCommand::StartPolling, | 347 | .command_argument = NFCCommand::StartPolling, |
| 251 | .packet_id = 0x0, | 348 | .block_id = {}, |
| 349 | .packet_id = {}, | ||
| 252 | .packet_flag = MCUPacketFlag::LastCommandPacket, | 350 | .packet_flag = MCUPacketFlag::LastCommandPacket, |
| 253 | .data_length = sizeof(NFCPollingCommandData), | 351 | .data_length = sizeof(NFCPollingCommandData), |
| 254 | .nfc_polling = | 352 | .nfc_polling = |
| 255 | { | 353 | { |
| 256 | .enable_mifare = 0x01, | 354 | .enable_mifare = 0x00, |
| 257 | .unknown_1 = 0x00, | 355 | .unknown_1 = static_cast<u8>(is_second_attempt ? 0xe8 : 0x00), |
| 258 | .unknown_2 = 0x00, | 356 | .unknown_2 = static_cast<u8>(is_second_attempt ? 0x03 : 0x00), |
| 259 | .unknown_3 = 0x2c, | 357 | .unknown_3 = 0x2c, |
| 260 | .unknown_4 = 0x01, | 358 | .unknown_4 = 0x01, |
| 261 | }, | 359 | }, |
| @@ -271,10 +369,11 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) { | |||
| 271 | 369 | ||
| 272 | DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { | 370 | DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { |
| 273 | NFCRequestState request{ | 371 | NFCRequestState request{ |
| 274 | .command_argument = NFCReadCommand::StopPolling, | 372 | .command_argument = NFCCommand::StopPolling, |
| 275 | .packet_id = 0x0, | 373 | .block_id = {}, |
| 374 | .packet_id = {}, | ||
| 276 | .packet_flag = MCUPacketFlag::LastCommandPacket, | 375 | .packet_flag = MCUPacketFlag::LastCommandPacket, |
| 277 | .data_length = 0, | 376 | .data_length = {}, |
| 278 | .raw_data = {}, | 377 | .raw_data = {}, |
| 279 | .crc = {}, | 378 | .crc = {}, |
| 280 | }; | 379 | }; |
| @@ -288,10 +387,11 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { | |||
| 288 | 387 | ||
| 289 | DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) { | 388 | DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) { |
| 290 | NFCRequestState request{ | 389 | NFCRequestState request{ |
| 291 | .command_argument = NFCReadCommand::StartWaitingRecieve, | 390 | .command_argument = NFCCommand::StartWaitingRecieve, |
| 391 | .block_id = {}, | ||
| 292 | .packet_id = packet_id, | 392 | .packet_id = packet_id, |
| 293 | .packet_flag = MCUPacketFlag::LastCommandPacket, | 393 | .packet_flag = MCUPacketFlag::LastCommandPacket, |
| 294 | .data_length = 0, | 394 | .data_length = {}, |
| 295 | .raw_data = {}, | 395 | .raw_data = {}, |
| 296 | .crc = {}, | 396 | .crc = {}, |
| 297 | }; | 397 | }; |
| @@ -305,17 +405,17 @@ DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 | |||
| 305 | 405 | ||
| 306 | DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { | 406 | DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { |
| 307 | NFCRequestState request{ | 407 | NFCRequestState request{ |
| 308 | .command_argument = NFCReadCommand::Ntag, | 408 | .command_argument = NFCCommand::ReadNtag, |
| 309 | .packet_id = 0x0, | 409 | .block_id = {}, |
| 410 | .packet_id = {}, | ||
| 310 | .packet_flag = MCUPacketFlag::LastCommandPacket, | 411 | .packet_flag = MCUPacketFlag::LastCommandPacket, |
| 311 | .data_length = sizeof(NFCReadCommandData), | 412 | .data_length = sizeof(NFCReadCommandData), |
| 312 | .nfc_read = | 413 | .nfc_read = |
| 313 | { | 414 | { |
| 314 | .unknown = 0xd0, | 415 | .unknown = 0xd0, |
| 315 | .uuid_length = 0x07, | 416 | .uuid_length = sizeof(NFCReadCommandData::uid), |
| 316 | .unknown_2 = 0x00, | ||
| 317 | .uid = {}, | 417 | .uid = {}, |
| 318 | .tag_type = NFCTagType::AllTags, | 418 | .tag_type = NFCTagType::Ntag215, |
| 319 | .read_block = GetReadBlockCommand(ntag_pages), | 419 | .read_block = GetReadBlockCommand(ntag_pages), |
| 320 | }, | 420 | }, |
| 321 | .crc = {}, | 421 | .crc = {}, |
| @@ -328,12 +428,135 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP | |||
| 328 | output); | 428 | output); |
| 329 | } | 429 | } |
| 330 | 430 | ||
| 431 | DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output, | ||
| 432 | const TagUUID& tag_uuid) { | ||
| 433 | NFCRequestState request{ | ||
| 434 | .command_argument = NFCCommand::ReadNtag, | ||
| 435 | .block_id = {}, | ||
| 436 | .packet_id = {}, | ||
| 437 | .packet_flag = MCUPacketFlag::LastCommandPacket, | ||
| 438 | .data_length = sizeof(NFCReadCommandData), | ||
| 439 | .nfc_read = | ||
| 440 | { | ||
| 441 | .unknown = 0xd0, | ||
| 442 | .uuid_length = sizeof(NFCReadCommandData::uid), | ||
| 443 | .uid = tag_uuid, | ||
| 444 | .tag_type = NFCTagType::Ntag215, | ||
| 445 | .read_block = GetReadBlockCommand(NFCPages::Block3), | ||
| 446 | }, | ||
| 447 | .crc = {}, | ||
| 448 | }; | ||
| 449 | |||
| 450 | std::array<u8, sizeof(NFCRequestState)> request_data{}; | ||
| 451 | memcpy(request_data.data(), &request, sizeof(NFCRequestState)); | ||
| 452 | request_data[36] = CalculateMCU_CRC8(request_data.data(), 36); | ||
| 453 | return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data, | ||
| 454 | output); | ||
| 455 | } | ||
| 456 | |||
| 457 | DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, | ||
| 458 | bool is_last_packet, | ||
| 459 | std::span<const u8> data) { | ||
| 460 | const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); | ||
| 461 | NFCRequestState request{ | ||
| 462 | .command_argument = NFCCommand::WriteNtag, | ||
| 463 | .block_id = block_id, | ||
| 464 | .packet_id = {}, | ||
| 465 | .packet_flag = | ||
| 466 | is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining, | ||
| 467 | .data_length = static_cast<u8>(data_size), | ||
| 468 | .raw_data = {}, | ||
| 469 | .crc = {}, | ||
| 470 | }; | ||
| 471 | memcpy(request.raw_data.data(), data.data(), data_size); | ||
| 472 | |||
| 473 | std::array<u8, sizeof(NFCRequestState)> request_data{}; | ||
| 474 | memcpy(request_data.data(), &request, sizeof(NFCRequestState)); | ||
| 475 | request_data[36] = CalculateMCU_CRC8(request_data.data(), 36); | ||
| 476 | return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data, | ||
| 477 | output); | ||
| 478 | } | ||
| 479 | |||
| 480 | std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const { | ||
| 481 | const std::size_t header_size = | ||
| 482 | sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); | ||
| 483 | std::vector<u8> serialized_data(header_size); | ||
| 484 | std::size_t start_index = 0; | ||
| 485 | |||
| 486 | memcpy(serialized_data.data(), &package, header_size); | ||
| 487 | start_index += header_size; | ||
| 488 | |||
| 489 | for (const auto& data_chunk : package.data_chunks) { | ||
| 490 | const std::size_t chunk_size = | ||
| 491 | sizeof(NFCDataChunk::nfc_page) + sizeof(NFCDataChunk::data_size) + data_chunk.data_size; | ||
| 492 | |||
| 493 | serialized_data.resize(start_index + chunk_size); | ||
| 494 | memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); | ||
| 495 | start_index += chunk_size; | ||
| 496 | } | ||
| 497 | |||
| 498 | return serialized_data; | ||
| 499 | } | ||
| 500 | |||
| 501 | NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, | ||
| 502 | std::span<const u8> data) const { | ||
| 503 | return { | ||
| 504 | .command_data{ | ||
| 505 | .unknown = 0xd0, | ||
| 506 | .uuid_length = sizeof(NFCReadCommandData::uid), | ||
| 507 | .uid = tag_uuid, | ||
| 508 | .tag_type = NFCTagType::Ntag215, | ||
| 509 | .unknown2 = 0x00, | ||
| 510 | .unknown3 = 0x01, | ||
| 511 | .unknown4 = 0x04, | ||
| 512 | .unknown5 = 0xff, | ||
| 513 | .unknown6 = 0xff, | ||
| 514 | .unknown7 = 0xff, | ||
| 515 | .unknown8 = 0xff, | ||
| 516 | .magic = data[16], | ||
| 517 | .write_count = static_cast<u16>((data[17] << 8) + data[18]), | ||
| 518 | .amiibo_version = data[19], | ||
| 519 | }, | ||
| 520 | .number_of_chunks = 3, | ||
| 521 | .data_chunks = | ||
| 522 | { | ||
| 523 | MakeAmiiboChunk(0x05, 0x20, data), | ||
| 524 | MakeAmiiboChunk(0x20, 0xf0, data), | ||
| 525 | MakeAmiiboChunk(0x5c, 0x98, data), | ||
| 526 | }, | ||
| 527 | }; | ||
| 528 | } | ||
| 529 | |||
| 530 | NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const { | ||
| 531 | constexpr u8 PAGE_SIZE = 4; | ||
| 532 | |||
| 533 | if (static_cast<std::size_t>(page * PAGE_SIZE) + size >= data.size()) { | ||
| 534 | return {}; | ||
| 535 | } | ||
| 536 | |||
| 537 | NFCDataChunk chunk{ | ||
| 538 | .nfc_page = page, | ||
| 539 | .data_size = size, | ||
| 540 | .data = {}, | ||
| 541 | }; | ||
| 542 | std::memcpy(chunk.data.data(), data.data() + (page * PAGE_SIZE), size); | ||
| 543 | return chunk; | ||
| 544 | } | ||
| 545 | |||
| 331 | NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { | 546 | NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { |
| 332 | switch (pages) { | 547 | switch (pages) { |
| 333 | case NFCPages::Block0: | 548 | case NFCPages::Block0: |
| 334 | return { | 549 | return { |
| 335 | .block_count = 1, | 550 | .block_count = 1, |
| 336 | }; | 551 | }; |
| 552 | case NFCPages::Block3: | ||
| 553 | return { | ||
| 554 | .block_count = 1, | ||
| 555 | .blocks = | ||
| 556 | { | ||
| 557 | NFCReadBlock{0x03, 0x03}, | ||
| 558 | }, | ||
| 559 | }; | ||
| 337 | case NFCPages::Block45: | 560 | case NFCPages::Block45: |
| 338 | return { | 561 | return { |
| 339 | .block_count = 1, | 562 | .block_count = 1, |
| @@ -368,6 +591,17 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { | |||
| 368 | }; | 591 | }; |
| 369 | } | 592 | } |
| 370 | 593 | ||
| 594 | TagUUID NfcProtocol::GetTagUUID(std::span<const u8> data) const { | ||
| 595 | if (data.size() < 10) { | ||
| 596 | return {}; | ||
| 597 | } | ||
| 598 | |||
| 599 | // crc byte 3 is omitted in this operation | ||
| 600 | return { | ||
| 601 | data[0], data[1], data[2], data[4], data[5], data[6], data[7], | ||
| 602 | }; | ||
| 603 | } | ||
| 604 | |||
| 371 | bool NfcProtocol::IsEnabled() const { | 605 | bool NfcProtocol::IsEnabled() const { |
| 372 | return is_enabled; | 606 | return is_enabled; |
| 373 | } | 607 | } |
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index c9e9af03f..eb58c427d 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h | |||
| @@ -27,6 +27,8 @@ public: | |||
| 27 | 27 | ||
| 28 | DriverResult ScanAmiibo(std::vector<u8>& data); | 28 | DriverResult ScanAmiibo(std::vector<u8>& data); |
| 29 | 29 | ||
| 30 | DriverResult WriteAmiibo(std::span<const u8> data); | ||
| 31 | |||
| 30 | bool HasAmiibo(); | 32 | bool HasAmiibo(); |
| 31 | 33 | ||
| 32 | bool IsEnabled() const; | 34 | bool IsEnabled() const; |
| @@ -37,18 +39,20 @@ private: | |||
| 37 | 39 | ||
| 38 | struct TagFoundData { | 40 | struct TagFoundData { |
| 39 | u8 type; | 41 | u8 type; |
| 40 | std::vector<u8> uuid; | 42 | u8 uuid_size; |
| 43 | TagUUID uuid; | ||
| 41 | }; | 44 | }; |
| 42 | 45 | ||
| 43 | DriverResult WaitUntilNfcIsReady(); | 46 | DriverResult WaitUntilNfcIs(NFCStatus status); |
| 44 | |||
| 45 | DriverResult WaitUntilNfcIsPolling(); | ||
| 46 | 47 | ||
| 47 | DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1); | 48 | DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1); |
| 48 | 49 | ||
| 49 | DriverResult GetAmiiboData(std::vector<u8>& data); | 50 | DriverResult GetAmiiboData(std::vector<u8>& data); |
| 50 | 51 | ||
| 51 | DriverResult SendStartPollingRequest(MCUCommandResponse& output); | 52 | DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); |
| 53 | |||
| 54 | DriverResult SendStartPollingRequest(MCUCommandResponse& output, | ||
| 55 | bool is_second_attempt = false); | ||
| 52 | 56 | ||
| 53 | DriverResult SendStopPollingRequest(MCUCommandResponse& output); | 57 | DriverResult SendStopPollingRequest(MCUCommandResponse& output); |
| 54 | 58 | ||
| @@ -56,8 +60,21 @@ private: | |||
| 56 | 60 | ||
| 57 | DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages); | 61 | DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages); |
| 58 | 62 | ||
| 63 | DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output, const TagUUID& tag_uuid); | ||
| 64 | |||
| 65 | DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, | ||
| 66 | bool is_last_packet, std::span<const u8> data); | ||
| 67 | |||
| 68 | std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; | ||
| 69 | |||
| 70 | NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const; | ||
| 71 | |||
| 72 | NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; | ||
| 73 | |||
| 59 | NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; | 74 | NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; |
| 60 | 75 | ||
| 76 | TagUUID GetTagUUID(std::span<const u8> data) const; | ||
| 77 | |||
| 61 | bool is_enabled{}; | 78 | bool is_enabled{}; |
| 62 | std::size_t update_counter{}; | 79 | std::size_t update_counter{}; |
| 63 | }; | 80 | }; |