diff options
Diffstat (limited to '')
-rw-r--r-- | src/input_common/drivers/joycon.cpp | 133 | ||||
-rw-r--r-- | src/input_common/drivers/joycon.h | 18 | ||||
-rw-r--r-- | src/input_common/drivers/sdl_driver.cpp | 32 | ||||
-rw-r--r-- | src/input_common/drivers/virtual_amiibo.cpp | 132 | ||||
-rw-r--r-- | src/input_common/drivers/virtual_amiibo.h | 17 |
5 files changed, 298 insertions, 34 deletions
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) { OnMotionUpdate(port, type, id, value); }}, .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, - .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) { - OnAmiiboUpdate(port, type, amiibo_data); + .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) { + OnAmiiboUpdate(port, type, tag_info); }}, .on_camera_data = {[this, port](const std::vector<u8>& camera_data, Joycon::IrsResolution format) { @@ -291,13 +291,105 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c return Common::Input::NfcState::Success; }; +Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + return TranslateDriverResult(handle->StartNfcPolling()); +}; + +Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + return TranslateDriverResult(handle->StopNfcPolling()); +}; + +Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier, + std::vector<u8>& out_data) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + return TranslateDriverResult(handle->ReadAmiiboData(out_data)); +} + Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier, const std::vector<u8>& data) { auto handle = GetHandle(identifier); - if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { - return Common::Input::NfcState::WriteFailed; + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; } - return Common::Input::NfcState::Success; + return TranslateDriverResult(handle->WriteNfcData(data)); +}; + +Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& data) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + + const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); + std::vector<Joycon::MifareReadChunk> read_request{}; + for (const auto& request_data : request.data) { + if (request_data.command == 0) { + continue; + } + Joycon::MifareReadChunk chunk = { + .command = command, + .sector_key = {}, + .sector = request_data.sector, + }; + memcpy(chunk.sector_key.data(), request_data.key.data(), + sizeof(Joycon::MifareReadChunk::sector_key)); + read_request.emplace_back(chunk); + } + + std::vector<Joycon::MifareReadData> read_data(read_request.size()); + const auto result = handle->ReadMifareData(read_request, read_data); + if (result == Joycon::DriverResult::Success) { + for (std::size_t i = 0; i < read_request.size(); i++) { + data.data[i] = { + .command = static_cast<u8>(command), + .sector = read_data[i].sector, + .key = {}, + .data = read_data[i].data, + }; + } + } + return TranslateDriverResult(result); +}; + +Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::NfcState::Unknown; + } + + const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); + std::vector<Joycon::MifareWriteChunk> write_request{}; + for (const auto& request_data : request.data) { + if (request_data.command == 0) { + continue; + } + Joycon::MifareWriteChunk chunk = { + .command = command, + .sector_key = {}, + .sector = request_data.sector, + .data = {}, + }; + memcpy(chunk.sector_key.data(), request_data.key.data(), + sizeof(Joycon::MifareReadChunk::sector_key)); + memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data)); + write_request.emplace_back(chunk); + } + + return TranslateDriverResult(handle->WriteMifareData(write_request)); }; Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, @@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) { } void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, - const std::vector<u8>& amiibo_data) { + const Joycon::TagInfo& tag_info) { const auto identifier = GetIdentifier(port, type); - const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved - : Common::Input::NfcState::NewAmiibo; - SetNfc(identifier, {nfc_state, amiibo_data}); + const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved + : Common::Input::NfcState::NewAmiibo; + + const Common::Input::NfcStatus nfc_status{ + .state = nfc_state, + .uuid_length = tag_info.uuid_length, + .protocol = tag_info.protocol, + .tag_type = tag_info.tag_type, + .uuid = tag_info.uuid, + }; + + SetNfc(identifier, nfc_status); } void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, @@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const { return "Unknown Switch Controller"; } } + +Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const { + switch (result) { + case Joycon::DriverResult::Success: + return Common::Input::NfcState::Success; + case Joycon::DriverResult::Disabled: + return Common::Input::NfcState::WrongDeviceState; + case Joycon::DriverResult::NotSupported: + return Common::Input::NfcState::NotSupported; + default: + return Common::Input::NfcState::Unknown; + } +} + } // 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>; struct Battery; struct Color; struct MotionData; +struct TagInfo; enum class ControllerType : u8; enum class DriverResult; enum class IrsResolution; @@ -39,9 +40,18 @@ public: Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, Common::Input::CameraFormat camera_format) override; - Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; - Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, + Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override; + Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override; + Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override; + Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier, + std::vector<u8>& out_data) override; + Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier, const std::vector<u8>& data) override; + Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& out_data) override; + Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier, + const Common::Input::MifareRequest& request) override; Common::Input::DriverResult SetPollingMode( const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; @@ -82,7 +92,7 @@ private: const Joycon::MotionData& value); void OnRingConUpdate(f32 ring_data); void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, - const std::vector<u8>& amiibo_data); + const Joycon::TagInfo& amiibo_data); void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, Joycon::IrsResolution format); @@ -102,6 +112,8 @@ private: /// Returns the name of the device in text format std::string JoyconName(Joycon::ControllerType type) const; + Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const; + std::jthread scan_thread; // Joycon types are split by type to ease supporting dualjoycon configurations diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 9a0439bb5..9f26392b1 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -150,6 +150,8 @@ public: if (sdl_controller) { const auto type = SDL_GameControllerGetType(sdl_controller.get()); return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || + (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) || + (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) || (type == SDL_CONTROLLER_TYPE_PS5); } return false; @@ -228,9 +230,8 @@ public: return false; } - Common::Input::BatteryLevel GetBatteryLevel() { - const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); - switch (level) { + Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) { + switch (battery_level) { case SDL_JOYSTICK_POWER_EMPTY: return Common::Input::BatteryLevel::Empty; case SDL_JOYSTICK_POWER_LOW: @@ -378,7 +379,6 @@ void SDLDriver::InitJoystick(int joystick_index) { if (joystick_map.find(guid) == joystick_map.end()) { auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); - SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); joystick->EnableMotion(); joystick_map[guid].emplace_back(std::move(joystick)); return; @@ -398,7 +398,6 @@ void SDLDriver::InitJoystick(int joystick_index) { const int port = static_cast<int>(joystick_guid_list.size()); auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); - SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); joystick->EnableMotion(); joystick_guid_list.emplace_back(std::move(joystick)); } @@ -438,8 +437,6 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetButton(identifier, event.jbutton.button, true); - // Battery doesn't trigger an event so just update every button press - SetBattery(identifier, joystick->GetBatteryLevel()); } break; } @@ -466,6 +463,13 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { } break; } + case SDL_JOYBATTERYUPDATED: { + if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) { + const PadIdentifier identifier = joystick->GetPadIdentifier(); + SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level)); + } + break; + } case SDL_JOYDEVICEREMOVED: LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); @@ -483,6 +487,10 @@ void SDLDriver::CloseJoysticks() { } SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) { + // Set our application name. Currently passed to DBus by SDL and visible to the user through + // their desktop environment. + SDL_SetHint(SDL_HINT_APP_NAME, "yuzu"); + if (!Settings::values.enable_raw_input) { // Disable raw input. When enabled this setting causes SDL to die when a web applet opens SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); @@ -501,6 +509,9 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); } else { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "1"); } // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled @@ -508,8 +519,11 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); } else { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); } + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1"); + // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native // driver on Linux. SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); @@ -789,7 +803,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p // This list also excludes Screenshot since there's not really a mapping for that ButtonBindings switch_to_sdl_button; - if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { + if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO || + SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT || + SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) { switch_to_sdl_button = GetNintendoButtonBinding(joystick); } else { switch_to_sdl_button = GetDefaultButtonBinding(); diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index f8bafe553..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( switch (polling_mode) { case Common::Input::PollingMode::NFC: - if (state == State::Initialized) { - state = State::WaitingForAmiibo; - } + state = State::Initialized; return Common::Input::DriverResult::Success; default: - if (state == State::AmiiboIsOpen) { + if (state == State::TagNearby) { CloseAmiibo(); } + state = State::Disabled; return Common::Input::DriverResult::NotSupported; } } @@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc( [[maybe_unused]] const PadIdentifier& identifier_) const { return Common::Input::NfcState::Success; } +Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) { + if (state != State::Initialized) { + return Common::Input::NfcState::WrongDeviceState; + } + state = State::WaitingForAmiibo; + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) { + if (state == State::Disabled) { + return Common::Input::NfcState::WrongDeviceState; + } + if (state == State::TagNearby) { + CloseAmiibo(); + } + state = State::Initialized; + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_, + std::vector<u8>& out_data) { + if (state != State::TagNearby) { + return Common::Input::NfcState::WrongDeviceState; + } + + if (status.tag_type != 1U << 1) { + return Common::Input::NfcState::InvalidTagType; + } + + out_data.resize(nfc_data.size()); + memcpy(out_data.data(), nfc_data.data(), nfc_data.size()); + return Common::Input::NfcState::Success; +} Common::Input::NfcState VirtualAmiibo::WriteNfcData( [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { @@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData( return Common::Input::NfcState::Success; } +Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_, + const Common::Input::MifareRequest& request, + Common::Input::MifareRequest& out_data) { + if (state != State::TagNearby) { + return Common::Input::NfcState::WrongDeviceState; + } + + if (status.tag_type != 1U << 6) { + return Common::Input::NfcState::InvalidTagType; + } + + for (std::size_t i = 0; i < request.data.size(); i++) { + if (request.data[i].command == 0) { + continue; + } + out_data.data[i].command = request.data[i].command; + out_data.data[i].sector = request.data[i].sector; + + const std::size_t sector_index = + request.data[i].sector * sizeof(Common::Input::MifareData::data); + + if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { + return Common::Input::NfcState::WriteFailed; + } + + // Ignore the sector key as we don't support it + memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index, + sizeof(Common::Input::MifareData::data)); + } + + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::WriteMifareData( + const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) { + if (state != State::TagNearby) { + return Common::Input::NfcState::WrongDeviceState; + } + + if (status.tag_type != 1U << 6) { + return Common::Input::NfcState::InvalidTagType; + } + + for (std::size_t i = 0; i < request.data.size(); i++) { + if (request.data[i].command == 0) { + continue; + } + + const std::size_t sector_index = + request.data[i].sector * sizeof(Common::Input::MifareData::data); + + if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { + return Common::Input::NfcState::WriteFailed; + } + + // Ignore the sector key as we don't support it + memcpy(nfc_data.data() + sector_index, request.data[i].data.data(), + sizeof(Common::Input::MifareData::data)); + } + + return Common::Input::NfcState::Success; +} + VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { return state; } @@ -82,6 +177,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { switch (nfc_file.GetSize()) { case AmiiboSize: case AmiiboSizeWithoutPassword: + case AmiiboSizeWithSignature: data.resize(AmiiboSize); if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) { return Info::NotAnAmiibo; @@ -109,24 +205,33 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) { switch (data.size_bytes()) { case AmiiboSize: case AmiiboSizeWithoutPassword: + case AmiiboSizeWithSignature: nfc_data.resize(AmiiboSize); + status.tag_type = 1U << 1; + status.uuid_length = 7; break; case MifareSize: nfc_data.resize(MifareSize); + status.tag_type = 1U << 6; + status.uuid_length = 4; break; default: return Info::NotAnAmiibo; } - state = State::AmiiboIsOpen; + status.uuid = {}; + status.protocol = 1; + state = State::TagNearby; + status.state = Common::Input::NfcState::NewAmiibo, memcpy(nfc_data.data(), data.data(), data.size_bytes()); - SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); + memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length); + SetNfc(identifier, status); return Info::Success; } VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { - if (state == State::AmiiboIsOpen) { - SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); + if (state == State::TagNearby) { + SetNfc(identifier, status); return Info::Success; } @@ -134,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { } VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { - state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo - : State::Initialized; - SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); + if (state != State::TagNearby) { + return Info::Success; + } + + state = State::WaitingForAmiibo; + status.state = Common::Input::NfcState::AmiiboRemoved; + SetNfc(identifier, status); + status.tag_type = 0; return Info::Success; } diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 34e97cd91..490f38e05 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -20,9 +20,10 @@ namespace InputCommon { class VirtualAmiibo final : public InputEngine { public: enum class State { + Disabled, Initialized, WaitingForAmiibo, - AmiiboIsOpen, + TagNearby, }; enum class Info { @@ -41,9 +42,17 @@ public: const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; - + Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override; + Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override; + Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_, + std::vector<u8>& out_data) override; Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, const std::vector<u8>& data) override; + Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_, + const Common::Input::MifareRequest& data, + Common::Input::MifareRequest& out_data) override; + Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_, + const Common::Input::MifareRequest& data) override; State GetCurrentState() const; @@ -57,11 +66,13 @@ public: private: static constexpr std::size_t AmiiboSize = 0x21C; static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8; + static constexpr std::size_t AmiiboSizeWithSignature = AmiiboSize + 0x20; static constexpr std::size_t MifareSize = 0x400; std::string file_path{}; - State state{State::Initialized}; + State state{State::Disabled}; std::vector<u8> nfc_data; + Common::Input::NfcStatus status; Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive}; }; } // namespace InputCommon |