input_common: Implement native mifare support
This commit is contained in:
parent
ec423c6919
commit
84d43489c5
25 changed files with 1170 additions and 198 deletions
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/helpers/joycon_driver.h"
|
||||
|
@ -112,7 +113,7 @@ DriverResult JoyconDriver::InitializeDevice() {
|
|||
joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
|
||||
right_stick_calibration, motion_calibration);
|
||||
|
||||
// Start pooling for data
|
||||
// Start polling for data
|
||||
is_connected = true;
|
||||
if (!input_thread_running) {
|
||||
input_thread =
|
||||
|
@ -208,7 +209,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
|
|||
joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
|
||||
}
|
||||
|
||||
if (nfc_protocol->IsEnabled()) {
|
||||
if (nfc_protocol->IsPolling()) {
|
||||
if (amiibo_detected) {
|
||||
if (!nfc_protocol->HasAmiibo()) {
|
||||
joycon_poller->UpdateAmiibo({});
|
||||
|
@ -218,10 +219,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
|
|||
}
|
||||
|
||||
if (!amiibo_detected) {
|
||||
std::vector<u8> data(0x21C);
|
||||
const auto result = nfc_protocol->ScanAmiibo(data);
|
||||
Joycon::TagInfo tag_info;
|
||||
const auto result = nfc_protocol->GetTagInfo(tag_info);
|
||||
if (result == DriverResult::Success) {
|
||||
joycon_poller->UpdateAmiibo(data);
|
||||
joycon_poller->UpdateAmiibo(tag_info);
|
||||
amiibo_detected = true;
|
||||
}
|
||||
}
|
||||
|
@ -247,6 +248,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
|
|||
}
|
||||
|
||||
DriverResult JoyconDriver::SetPollingMode() {
|
||||
SCOPE_EXIT({ disable_input_thread = false; });
|
||||
disable_input_thread = true;
|
||||
|
||||
rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
|
||||
|
@ -276,7 +278,6 @@ DriverResult JoyconDriver::SetPollingMode() {
|
|||
if (irs_enabled && supported_features.irs) {
|
||||
auto result = irs_protocol->EnableIrs();
|
||||
if (result == DriverResult::Success) {
|
||||
disable_input_thread = false;
|
||||
return result;
|
||||
}
|
||||
irs_protocol->DisableIrs();
|
||||
|
@ -286,10 +287,6 @@ DriverResult JoyconDriver::SetPollingMode() {
|
|||
if (nfc_enabled && supported_features.nfc) {
|
||||
auto result = nfc_protocol->EnableNfc();
|
||||
if (result == DriverResult::Success) {
|
||||
result = nfc_protocol->StartNFCPollingMode();
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
disable_input_thread = false;
|
||||
return result;
|
||||
}
|
||||
nfc_protocol->DisableNfc();
|
||||
|
@ -303,7 +300,6 @@ DriverResult JoyconDriver::SetPollingMode() {
|
|||
}
|
||||
if (result == DriverResult::Success) {
|
||||
ring_connected = true;
|
||||
disable_input_thread = false;
|
||||
return result;
|
||||
}
|
||||
ring_connected = false;
|
||||
|
@ -314,7 +310,6 @@ DriverResult JoyconDriver::SetPollingMode() {
|
|||
if (passive_enabled && supported_features.passive) {
|
||||
const auto result = generic_protocol->EnablePassiveMode();
|
||||
if (result == DriverResult::Success) {
|
||||
disable_input_thread = false;
|
||||
return result;
|
||||
}
|
||||
LOG_ERROR(Input, "Error enabling passive mode");
|
||||
|
@ -328,7 +323,6 @@ DriverResult JoyconDriver::SetPollingMode() {
|
|||
// Switch calls this function after enabling active mode
|
||||
generic_protocol->TriggersElapsed();
|
||||
|
||||
disable_input_thread = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -492,9 +486,42 @@ DriverResult JoyconDriver::SetRingConMode() {
|
|||
return result;
|
||||
}
|
||||
|
||||
DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
|
||||
DriverResult JoyconDriver::StartNfcPolling() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (!supported_features.nfc) {
|
||||
return DriverResult::NotSupported;
|
||||
}
|
||||
if (!nfc_protocol->IsEnabled()) {
|
||||
return DriverResult::Disabled;
|
||||
}
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->StartNFCPollingMode();
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult JoyconDriver::StopNfcPolling() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (!supported_features.nfc) {
|
||||
return DriverResult::NotSupported;
|
||||
}
|
||||
if (!nfc_protocol->IsEnabled()) {
|
||||
return DriverResult::Disabled;
|
||||
}
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->StopNFCPollingMode();
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (!supported_features.nfc) {
|
||||
return DriverResult::NotSupported;
|
||||
|
@ -506,9 +533,72 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
|
|||
return DriverResult::ErrorWritingData;
|
||||
}
|
||||
|
||||
const auto result = nfc_protocol->WriteAmiibo(data);
|
||||
|
||||
out_data.resize(0x21C);
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->ReadAmiibo(out_data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (!supported_features.nfc) {
|
||||
return DriverResult::NotSupported;
|
||||
}
|
||||
if (!nfc_protocol->IsEnabled()) {
|
||||
return DriverResult::Disabled;
|
||||
}
|
||||
if (!amiibo_detected) {
|
||||
return DriverResult::ErrorWritingData;
|
||||
}
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->WriteAmiibo(data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
|
||||
std::span<MifareReadData> out_data) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (!supported_features.nfc) {
|
||||
return DriverResult::NotSupported;
|
||||
}
|
||||
if (!nfc_protocol->IsEnabled()) {
|
||||
return DriverResult::Disabled;
|
||||
}
|
||||
if (!amiibo_detected) {
|
||||
return DriverResult::ErrorWritingData;
|
||||
}
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->ReadMifare(data, out_data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (!supported_features.nfc) {
|
||||
return DriverResult::NotSupported;
|
||||
}
|
||||
if (!nfc_protocol->IsEnabled()) {
|
||||
return DriverResult::Disabled;
|
||||
}
|
||||
if (!amiibo_detected) {
|
||||
return DriverResult::ErrorWritingData;
|
||||
}
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->WriteMifare(data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,13 @@ public:
|
|||
DriverResult SetIrMode();
|
||||
DriverResult SetNfcMode();
|
||||
DriverResult SetRingConMode();
|
||||
DriverResult StartNfcPolling();
|
||||
DriverResult StopNfcPolling();
|
||||
DriverResult ReadAmiiboData(std::vector<u8>& out_data);
|
||||
DriverResult WriteNfcData(std::span<const u8> data);
|
||||
DriverResult ReadMifareData(std::span<const MifareReadChunk> request,
|
||||
std::span<MifareReadData> out_data);
|
||||
DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);
|
||||
|
||||
void SetCallbacks(const JoyconCallbacks& callbacks);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x
|
|||
using MacAddress = std::array<u8, 6>;
|
||||
using SerialNumber = std::array<u8, 15>;
|
||||
using TagUUID = std::array<u8, 7>;
|
||||
using MifareUUID = std::array<u8, 4>;
|
||||
|
||||
enum class ControllerType : u8 {
|
||||
None = 0x00,
|
||||
|
@ -307,6 +308,19 @@ enum class NFCStatus : u8 {
|
|||
WriteDone = 0x05,
|
||||
TagLost = 0x07,
|
||||
WriteReady = 0x09,
|
||||
MifareDone = 0x10,
|
||||
};
|
||||
|
||||
enum class MifareCmd : u8 {
|
||||
None = 0x00,
|
||||
Read = 0x30,
|
||||
AuthA = 0x60,
|
||||
AuthB = 0x61,
|
||||
Write = 0xA0,
|
||||
Transfer = 0xB0,
|
||||
Decrement = 0xC0,
|
||||
Increment = 0xC1,
|
||||
Store = 0xC2
|
||||
};
|
||||
|
||||
enum class IrsMode : u8 {
|
||||
|
@ -592,6 +606,14 @@ struct NFCWriteCommandData {
|
|||
static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
|
||||
#pragma pack(pop)
|
||||
|
||||
struct MifareCommandData {
|
||||
u8 unknown1;
|
||||
u8 unknown2;
|
||||
u8 number_of_short_bytes;
|
||||
MifareUUID uid;
|
||||
};
|
||||
static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size");
|
||||
|
||||
struct NFCPollingCommandData {
|
||||
u8 enable_mifare;
|
||||
u8 unknown_1;
|
||||
|
@ -629,6 +651,41 @@ struct NFCWritePackage {
|
|||
std::array<NFCDataChunk, 4> data_chunks;
|
||||
};
|
||||
|
||||
struct MifareReadChunk {
|
||||
MifareCmd command;
|
||||
std::array<u8, 0x6> sector_key;
|
||||
u8 sector;
|
||||
};
|
||||
|
||||
struct MifareWriteChunk {
|
||||
MifareCmd command;
|
||||
std::array<u8, 0x6> sector_key;
|
||||
u8 sector;
|
||||
std::array<u8, 0x10> data;
|
||||
};
|
||||
|
||||
struct MifareReadData {
|
||||
u8 sector;
|
||||
std::array<u8, 0x10> data;
|
||||
};
|
||||
|
||||
struct MifareReadPackage {
|
||||
MifareCommandData command_data;
|
||||
std::array<MifareReadChunk, 0x10> data_chunks;
|
||||
};
|
||||
|
||||
struct MifareWritePackage {
|
||||
MifareCommandData command_data;
|
||||
std::array<MifareWriteChunk, 0x10> data_chunks;
|
||||
};
|
||||
|
||||
struct TagInfo {
|
||||
u8 uuid_length;
|
||||
u8 protocol;
|
||||
u8 tag_type;
|
||||
std::array<u8, 10> uuid;
|
||||
};
|
||||
|
||||
struct IrsConfigure {
|
||||
MCUCommand command;
|
||||
MCUSubCommand sub_command;
|
||||
|
@ -744,7 +801,7 @@ struct JoyconCallbacks {
|
|||
std::function<void(int, f32)> on_stick_data;
|
||||
std::function<void(int, const MotionData&)> on_motion_data;
|
||||
std::function<void(f32)> on_ring_data;
|
||||
std::function<void(const std::vector<u8>&)> on_amiibo_data;
|
||||
std::function<void(const Joycon::TagInfo&)> on_amiibo_data;
|
||||
std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,16 @@ DriverResult NfcProtocol::EnableNfc() {
|
|||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::Ready);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStopPollingRequest(output);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::Ready);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
is_enabled = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -54,22 +64,16 @@ DriverResult NfcProtocol::DisableNfc() {
|
|||
}
|
||||
|
||||
is_enabled = false;
|
||||
is_polling = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::StartNFCPollingMode() {
|
||||
LOG_DEBUG(Input, "Start NFC pooling Mode");
|
||||
LOG_DEBUG(Input, "Start NFC polling Mode");
|
||||
ScopedSetBlocking sb(this);
|
||||
DriverResult result{DriverResult::Success};
|
||||
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStopPollingRequest(output);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::Ready);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStartPollingRequest(output);
|
||||
|
@ -78,13 +82,32 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
|
|||
result = WaitUntilNfcIs(NFCStatus::Polling);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
is_enabled = true;
|
||||
is_polling = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
|
||||
DriverResult NfcProtocol::StopNFCPollingMode() {
|
||||
LOG_DEBUG(Input, "Stop NFC polling Mode");
|
||||
ScopedSetBlocking sb(this);
|
||||
DriverResult result{DriverResult::Success};
|
||||
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStopPollingRequest(output);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::WriteReady);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
is_polling = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {
|
||||
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
|
||||
return DriverResult::Delayed;
|
||||
}
|
||||
|
@ -100,11 +123,41 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
|
|||
}
|
||||
|
||||
if (result == DriverResult::Success) {
|
||||
tag_info = {
|
||||
.uuid_length = tag_data.uuid_size,
|
||||
.protocol = 1,
|
||||
.tag_type = tag_data.type,
|
||||
.uuid = {},
|
||||
};
|
||||
|
||||
memcpy(tag_info.uuid.data(), tag_data.uuid.data(), tag_data.uuid_size);
|
||||
|
||||
// Investigate why mifare type is not correct
|
||||
if (tag_info.tag_type == 144) {
|
||||
tag_info.tag_type = 1U << 6;
|
||||
}
|
||||
|
||||
std::string uuid_string;
|
||||
for (auto& content : tag_data.uuid) {
|
||||
uuid_string += fmt::format(" {:02x}", content);
|
||||
}
|
||||
LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) {
|
||||
LOG_DEBUG(Input, "Scan for amiibos");
|
||||
ScopedSetBlocking sb(this);
|
||||
DriverResult result{DriverResult::Success};
|
||||
TagFoundData tag_data{};
|
||||
|
||||
if (result == DriverResult::Success) {
|
||||
result = IsTagInRange(tag_data, 7);
|
||||
}
|
||||
|
||||
if (result == DriverResult::Success) {
|
||||
result = GetAmiiboData(data);
|
||||
}
|
||||
|
||||
|
@ -154,6 +207,69 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
|
|||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request,
|
||||
std::span<MifareReadData> out_data) {
|
||||
LOG_DEBUG(Input, "Read mifare");
|
||||
ScopedSetBlocking sb(this);
|
||||
DriverResult result{DriverResult::Success};
|
||||
TagFoundData tag_data{};
|
||||
MifareUUID tag_uuid{};
|
||||
|
||||
if (result == DriverResult::Success) {
|
||||
result = IsTagInRange(tag_data, 7);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
|
||||
result = GetMifareData(tag_uuid, read_request, out_data);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStopPollingRequest(output);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::Ready);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStartPollingRequest(output, true);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::WriteReady);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) {
|
||||
LOG_DEBUG(Input, "Write mifare");
|
||||
ScopedSetBlocking sb(this);
|
||||
DriverResult result{DriverResult::Success};
|
||||
TagFoundData tag_data{};
|
||||
MifareUUID tag_uuid{};
|
||||
|
||||
if (result == DriverResult::Success) {
|
||||
result = IsTagInRange(tag_data, 7);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID));
|
||||
result = WriteMifareData(tag_uuid, write_request);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStopPollingRequest(output);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::Ready);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
MCUCommandResponse output{};
|
||||
result = SendStartPollingRequest(output, true);
|
||||
}
|
||||
if (result == DriverResult::Success) {
|
||||
result = WaitUntilNfcIs(NFCStatus::WriteReady);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool NfcProtocol::HasAmiibo() {
|
||||
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
|
||||
return true;
|
||||
|
@ -341,6 +457,158 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con
|
|||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid,
|
||||
std::span<const MifareReadChunk> read_request,
|
||||
std::span<MifareReadData> out_data) {
|
||||
constexpr std::size_t timeout_limit = 60;
|
||||
const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request);
|
||||
const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data);
|
||||
std::span<const u8> buffer(nfc_buffer_data);
|
||||
DriverResult result = DriverResult::Success;
|
||||
MCUCommandResponse output{};
|
||||
u8 block_id = 1;
|
||||
u8 package_index = 0;
|
||||
std::size_t tries = 0;
|
||||
std::size_t current_position = 0;
|
||||
|
||||
LOG_INFO(Input, "Reading Mifare data");
|
||||
|
||||
// Send data request. Nfc buffer size is 31, Send the data in smaller packages
|
||||
while (current_position < buffer.size() && tries++ < timeout_limit) {
|
||||
const std::size_t next_position =
|
||||
std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
|
||||
const std::size_t block_size = next_position - current_position;
|
||||
const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
|
||||
|
||||
SendReadDataMifareRequest(output, block_id, is_last_packet,
|
||||
buffer.subspan(current_position, block_size));
|
||||
|
||||
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
|
||||
return DriverResult::ErrorReadingData;
|
||||
}
|
||||
|
||||
// Increase position when data is confirmed by the joycon
|
||||
if (output.mcu_report == MCUReport::NFCState &&
|
||||
(output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
|
||||
output.mcu_data[3] == block_id) {
|
||||
block_id++;
|
||||
current_position = next_position;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != DriverResult::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Wait for reply and save the output data
|
||||
while (tries++ < timeout_limit) {
|
||||
result = SendNextPackageRequest(output, package_index);
|
||||
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
|
||||
|
||||
if (result != DriverResult::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
|
||||
return DriverResult::ErrorReadingData;
|
||||
}
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
|
||||
constexpr std::size_t DATA_LENGHT = 0x10 + 1;
|
||||
constexpr std::size_t DATA_START = 11;
|
||||
const u8 number_of_elements = output.mcu_data[10];
|
||||
for (std::size_t i = 0; i < number_of_elements; i++) {
|
||||
out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)];
|
||||
memcpy(out_data[i].data.data(),
|
||||
output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT),
|
||||
sizeof(MifareReadData::data));
|
||||
}
|
||||
package_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
|
||||
LOG_INFO(Input, "Finished reading mifare");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid,
|
||||
std::span<const MifareWriteChunk> write_request) {
|
||||
constexpr std::size_t timeout_limit = 60;
|
||||
const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request);
|
||||
const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data);
|
||||
std::span<const u8> buffer(nfc_buffer_data);
|
||||
DriverResult result = DriverResult::Success;
|
||||
MCUCommandResponse output{};
|
||||
u8 block_id = 1;
|
||||
u8 package_index = 0;
|
||||
std::size_t tries = 0;
|
||||
std::size_t current_position = 0;
|
||||
|
||||
LOG_INFO(Input, "Writing Mifare data");
|
||||
|
||||
// Send data request. Nfc buffer size is 31, Send the data in smaller packages
|
||||
while (current_position < buffer.size() && tries++ < timeout_limit) {
|
||||
const std::size_t next_position =
|
||||
std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
|
||||
const std::size_t block_size = next_position - current_position;
|
||||
const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
|
||||
|
||||
SendReadDataMifareRequest(output, block_id, is_last_packet,
|
||||
buffer.subspan(current_position, block_size));
|
||||
|
||||
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
|
||||
return DriverResult::ErrorReadingData;
|
||||
}
|
||||
|
||||
// Increase position when data is confirmed by the joycon
|
||||
if (output.mcu_report == MCUReport::NFCState &&
|
||||
(output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
|
||||
output.mcu_data[3] == block_id) {
|
||||
block_id++;
|
||||
current_position = next_position;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != DriverResult::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Wait for reply and ignore the output data
|
||||
while (tries++ < timeout_limit) {
|
||||
result = SendNextPackageRequest(output, package_index);
|
||||
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
|
||||
|
||||
if (result != DriverResult::Success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) {
|
||||
return DriverResult::ErrorReadingData;
|
||||
}
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
|
||||
package_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) {
|
||||
LOG_INFO(Input, "Finished writing mifare");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
|
||||
bool is_second_attempt) {
|
||||
NFCRequestState request{
|
||||
|
@ -477,6 +745,28 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,
|
|||
output);
|
||||
}
|
||||
|
||||
DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
|
||||
bool is_last_packet, std::span<const u8> data) {
|
||||
const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
|
||||
NFCRequestState request{
|
||||
.command_argument = NFCCommand::Mifare,
|
||||
.block_id = block_id,
|
||||
.packet_id = {},
|
||||
.packet_flag =
|
||||
is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining,
|
||||
.data_length = static_cast<u8>(data_size),
|
||||
.raw_data = {},
|
||||
.crc = {},
|
||||
};
|
||||
memcpy(request.raw_data.data(), data.data(), data_size);
|
||||
|
||||
std::array<u8, sizeof(NFCRequestState)> request_data{};
|
||||
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
|
||||
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
|
||||
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
|
||||
output);
|
||||
}
|
||||
|
||||
std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {
|
||||
const std::size_t header_size =
|
||||
sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks);
|
||||
|
@ -498,6 +788,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag
|
|||
return serialized_data;
|
||||
}
|
||||
|
||||
std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const {
|
||||
const std::size_t header_size = sizeof(MifareCommandData);
|
||||
std::vector<u8> serialized_data(header_size);
|
||||
std::size_t start_index = 0;
|
||||
|
||||
memcpy(serialized_data.data(), &package, header_size);
|
||||
start_index += header_size;
|
||||
|
||||
for (const auto& data_chunk : package.data_chunks) {
|
||||
const std::size_t chunk_size = sizeof(MifareReadChunk);
|
||||
if (data_chunk.command == MifareCmd::None) {
|
||||
continue;
|
||||
}
|
||||
serialized_data.resize(start_index + chunk_size);
|
||||
memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
|
||||
start_index += chunk_size;
|
||||
}
|
||||
|
||||
return serialized_data;
|
||||
}
|
||||
|
||||
std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const {
|
||||
const std::size_t header_size = sizeof(MifareCommandData);
|
||||
std::vector<u8> serialized_data(header_size);
|
||||
std::size_t start_index = 0;
|
||||
|
||||
memcpy(serialized_data.data(), &package, header_size);
|
||||
start_index += header_size;
|
||||
|
||||
for (const auto& data_chunk : package.data_chunks) {
|
||||
const std::size_t chunk_size = sizeof(MifareWriteChunk);
|
||||
if (data_chunk.command == MifareCmd::None) {
|
||||
continue;
|
||||
}
|
||||
serialized_data.resize(start_index + chunk_size);
|
||||
memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
|
||||
start_index += chunk_size;
|
||||
}
|
||||
|
||||
return serialized_data;
|
||||
}
|
||||
|
||||
NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
|
||||
std::span<const u8> data) const {
|
||||
return {
|
||||
|
@ -527,6 +859,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
|
|||
};
|
||||
}
|
||||
|
||||
MifareReadPackage NfcProtocol::MakeMifareReadPackage(
|
||||
const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const {
|
||||
MifareReadPackage package{
|
||||
.command_data{
|
||||
.unknown1 = 0xd0,
|
||||
.unknown2 = 0x07,
|
||||
.number_of_short_bytes = static_cast<u8>(
|
||||
((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2),
|
||||
.uid = tag_uuid,
|
||||
},
|
||||
.data_chunks = {},
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
|
||||
package.data_chunks[i] = read_request[i];
|
||||
}
|
||||
|
||||
return package;
|
||||
}
|
||||
|
||||
MifareWritePackage NfcProtocol::MakeMifareWritePackage(
|
||||
const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const {
|
||||
MifareWritePackage package{
|
||||
.command_data{
|
||||
.unknown1 = 0xd0,
|
||||
.unknown2 = 0x07,
|
||||
.number_of_short_bytes = static_cast<u8>(
|
||||
((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2),
|
||||
.uid = tag_uuid,
|
||||
},
|
||||
.data_chunks = {},
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) {
|
||||
package.data_chunks[i] = read_request[i];
|
||||
}
|
||||
|
||||
return package;
|
||||
}
|
||||
|
||||
NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
|
||||
constexpr u8 NFC_PAGE_SIZE = 4;
|
||||
|
||||
|
@ -606,4 +978,8 @@ bool NfcProtocol::IsEnabled() const {
|
|||
return is_enabled;
|
||||
}
|
||||
|
||||
bool NfcProtocol::IsPolling() const {
|
||||
return is_polling;
|
||||
}
|
||||
|
||||
} // namespace InputCommon::Joycon
|
||||
|
|
|
@ -25,14 +25,25 @@ public:
|
|||
|
||||
DriverResult StartNFCPollingMode();
|
||||
|
||||
DriverResult ScanAmiibo(std::vector<u8>& data);
|
||||
DriverResult StopNFCPollingMode();
|
||||
|
||||
DriverResult GetTagInfo(Joycon::TagInfo& tag_info);
|
||||
|
||||
DriverResult ReadAmiibo(std::vector<u8>& data);
|
||||
|
||||
DriverResult WriteAmiibo(std::span<const u8> data);
|
||||
|
||||
DriverResult ReadMifare(std::span<const MifareReadChunk> read_request,
|
||||
std::span<MifareReadData> out_data);
|
||||
|
||||
DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request);
|
||||
|
||||
bool HasAmiibo();
|
||||
|
||||
bool IsEnabled() const;
|
||||
|
||||
bool IsPolling() const;
|
||||
|
||||
private:
|
||||
// Number of times the function will be delayed until it outputs valid data
|
||||
static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15;
|
||||
|
@ -51,6 +62,13 @@ private:
|
|||
|
||||
DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
|
||||
|
||||
DriverResult GetMifareData(const MifareUUID& tag_uuid,
|
||||
std::span<const MifareReadChunk> read_request,
|
||||
std::span<MifareReadData> out_data);
|
||||
|
||||
DriverResult WriteMifareData(const MifareUUID& tag_uuid,
|
||||
std::span<const MifareWriteChunk> write_request);
|
||||
|
||||
DriverResult SendStartPollingRequest(MCUCommandResponse& output,
|
||||
bool is_second_attempt = false);
|
||||
|
||||
|
@ -65,17 +83,31 @@ private:
|
|||
DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
|
||||
bool is_last_packet, std::span<const u8> data);
|
||||
|
||||
DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id,
|
||||
bool is_last_packet, std::span<const u8> data);
|
||||
|
||||
std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
|
||||
|
||||
std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const;
|
||||
|
||||
std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const;
|
||||
|
||||
NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;
|
||||
|
||||
NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const;
|
||||
|
||||
MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid,
|
||||
std::span<const MifareReadChunk> read_request) const;
|
||||
|
||||
MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid,
|
||||
std::span<const MifareWriteChunk> read_request) const;
|
||||
|
||||
NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
|
||||
|
||||
TagUUID GetTagUUID(std::span<const u8> data) const;
|
||||
|
||||
bool is_enabled{};
|
||||
bool is_polling{};
|
||||
std::size_t update_counter{};
|
||||
};
|
||||
|
||||
|
|
|
@ -70,8 +70,8 @@ void JoyconPoller::UpdateColor(const Color& color) {
|
|||
callbacks.on_color_data(color);
|
||||
}
|
||||
|
||||
void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) {
|
||||
callbacks.on_amiibo_data(amiibo_data);
|
||||
void JoyconPoller::UpdateAmiibo(const Joycon::TagInfo& tag_info) {
|
||||
callbacks.on_amiibo_data(tag_info);
|
||||
}
|
||||
|
||||
void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
|
||||
|
|
|
@ -36,8 +36,8 @@ public:
|
|||
|
||||
void UpdateColor(const Color& color);
|
||||
void UpdateRing(s16 value, const RingStatus& ring_status);
|
||||
void UpdateAmiibo(const std::vector<u8>& amiibo_data);
|
||||
void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format);
|
||||
void UpdateAmiibo(const Joycon::TagInfo& tag_info);
|
||||
void UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format);
|
||||
|
||||
private:
|
||||
void UpdateActiveLeftPadInput(const InputReportActive& input,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue