From d80e6c399bf8196646cca5ac1265d122638bb96b Mon Sep 17 00:00:00 2001 From: Narr the Reg <juangerman-13@hotmail.com> Date: Tue, 20 Dec 2022 11:34:33 -0600 Subject: input_common: Initial skeleton for custom joycon driver --- src/input_common/drivers/joycon.cpp | 615 ++++++++++++++++++++++++++++++++++++ 1 file changed, 615 insertions(+) create mode 100644 src/input_common/drivers/joycon.cpp (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp new file mode 100644 index 0000000000..eab10d11c0 --- /dev/null +++ b/src/input_common/drivers/joycon.cpp @@ -0,0 +1,615 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <fmt/format.h> + +#include "common/param_package.h" +#include "common/settings.h" +#include "common/thread.h" +#include "input_common/drivers/joycon.h" +#include "input_common/helpers/joycon_driver.h" +#include "input_common/helpers/joycon_protocol/joycon_types.h" + +namespace InputCommon { + +Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) { + LOG_INFO(Input, "Joycon driver Initialization started"); + const int init_res = SDL_hid_init(); + if (init_res == 0) { + Setup(); + } else { + LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res); + } +} + +Joycons::~Joycons() { + Reset(); +} + +void Joycons::Reset() { + scan_thread = {}; + for (const auto& device : left_joycons) { + if (!device) { + continue; + } + device->Stop(); + } + for (const auto& device : right_joycons) { + if (!device) { + continue; + } + device->Stop(); + } + for (const auto& device : pro_joycons) { + if (!device) { + continue; + } + device->Stop(); + } + SDL_hid_exit(); +} + +void Joycons::Setup() { + u32 port = 0; + for (auto& device : left_joycons) { + PreSetController(GetIdentifier(port, Joycon::ControllerType::Left)); + device = std::make_shared<Joycon::JoyconDriver>(port++); + } + for (auto& device : right_joycons) { + PreSetController(GetIdentifier(port, Joycon::ControllerType::Right)); + device = std::make_shared<Joycon::JoyconDriver>(port++); + } + for (auto& device : pro_joycons) { + PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro)); + device = std::make_shared<Joycon::JoyconDriver>(port++); + } + + if (!scan_thread_running) { + scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); }); + } +} + +void Joycons::ScanThread(std::stop_token stop_token) { + constexpr u16 nintendo_vendor_id = 0x057e; + Common::SetCurrentThreadName("yuzu:input:JoyconScanThread"); + scan_thread_running = true; + while (!stop_token.stop_requested()) { + SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0); + SDL_hid_device_info* cur_dev = devs; + + while (cur_dev) { + if (IsDeviceNew(cur_dev)) { + LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id, + cur_dev->product_id); + RegisterNewDevice(cur_dev); + } + cur_dev = cur_dev->next; + } + + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + scan_thread_running = false; +} + +bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { + Joycon::ControllerType type{}; + Joycon::SerialNumber serial_number{}; + + const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type); + if (result != Joycon::DriverResult::Success) { + return false; + } + + const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number); + if (result2 != Joycon::DriverResult::Success) { + return false; + } + + auto is_handle_identical = [&](std::shared_ptr<Joycon::JoyconDriver> device) { + if (!device) { + return false; + } + if (!device->IsConnected()) { + return false; + } + if (device->GetHandleSerialNumber() != serial_number) { + return false; + } + return true; + }; + + // Check if device already exist + switch (type) { + case Joycon::ControllerType::Left: + for (const auto& device : left_joycons) { + if (is_handle_identical(device)) { + return false; + } + } + break; + case Joycon::ControllerType::Right: + for (const auto& device : right_joycons) { + if (is_handle_identical(device)) { + return false; + } + } + break; + case Joycon::ControllerType::Pro: + case Joycon::ControllerType::Grip: + for (const auto& device : pro_joycons) { + if (is_handle_identical(device)) { + return false; + } + } + break; + default: + return false; + } + + return true; +} + +void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) { + Joycon::ControllerType type{}; + auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type); + auto handle = GetNextFreeHandle(type); + if (handle == nullptr) { + LOG_WARNING(Input, "No free handles available"); + return; + } + if (result == Joycon::DriverResult::Success) { + result = handle->RequestDeviceAccess(device_info); + } + if (result == Joycon::DriverResult::Success) { + LOG_WARNING(Input, "Initialize device"); + + std::function<void(Joycon::Battery)> on_battery_data; + std::function<void(Joycon::Color)> on_button_data; + std::function<void(int, f32)> on_stick_data; + std::function<void(int, std::array<u8, 6>)> on_motion_data; + std::function<void(s16)> on_ring_data; + std::function<void(const std::vector<u8>&)> on_amiibo_data; + + const std::size_t port = handle->GetDevicePort(); + handle->on_battery_data = { + [this, port, type](Joycon::Battery value) { OnBatteryUpdate(port, type, value); }}; + handle->on_color_data = { + [this, port, type](Joycon::Color value) { OnColorUpdate(port, type, value); }}; + handle->on_button_data = { + [this, port, type](int id, bool value) { OnButtonUpdate(port, type, id, value); }}; + handle->on_stick_data = { + [this, port, type](int id, f32 value) { OnStickUpdate(port, type, id, value); }}; + handle->on_motion_data = {[this, port, type](int id, Joycon::MotionData value) { + OnMotionUpdate(port, type, id, value); + }}; + handle->on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}; + handle->on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) { + OnAmiiboUpdate(port, amiibo_data); + }}; + handle->InitializeDevice(); + } +} + +std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle( + Joycon::ControllerType type) const { + + if (type == Joycon::ControllerType::Left) { + for (const auto& device : left_joycons) { + if (!device->IsConnected()) { + return device; + } + } + } + if (type == Joycon::ControllerType::Right) { + for (const auto& device : right_joycons) { + if (!device->IsConnected()) { + return device; + } + } + } + if (type == Joycon::ControllerType::Pro || type == Joycon::ControllerType::Grip) { + for (const auto& device : pro_joycons) { + if (!device->IsConnected()) { + return device; + } + } + } + return nullptr; +} + +bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) { + const auto handle = GetHandle(identifier); + if (handle == nullptr) { + return false; + } + return handle->IsVibrationEnabled(); +} + +Common::Input::VibrationError Joycons::SetVibration( + const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { + const Joycon::VibrationValue native_vibration{ + .low_amplitude = vibration.low_amplitude, + .low_frequency = vibration.low_frequency, + .high_amplitude = vibration.high_amplitude, + .high_frequency = vibration.high_amplitude, + }; + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::VibrationError::InvalidHandle; + } + + handle->SetVibration(native_vibration); + return Common::Input::VibrationError::None; +} + +void Joycons::SetLeds(const PadIdentifier& identifier, const Common::Input::LedStatus& led_status) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return; + } + int led_config = led_status.led_1 ? 1 : 0; + led_config += led_status.led_2 ? 2 : 0; + led_config += led_status.led_3 ? 4 : 0; + led_config += led_status.led_4 ? 8 : 0; + + const auto result = handle->SetLedConfig(static_cast<u8>(led_config)); + if (result != Joycon::DriverResult::Success) { + LOG_ERROR(Input, "Failed to set led config"); + } +} + +Common::Input::CameraError Joycons::SetCameraFormat(const PadIdentifier& identifier_, + Common::Input::CameraFormat camera_format) { + return Common::Input::CameraError::NotSupported; +}; + +Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const { + return Common::Input::NfcState::Success; +}; + +Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_, + const std::vector<u8>& data) { + return Common::Input::NfcState::NotSupported; +}; + +Common::Input::PollingError Joycons::SetPollingMode(const PadIdentifier& identifier, + const Common::Input::PollingMode polling_mode) { + auto handle = GetHandle(identifier); + if (handle == nullptr) { + LOG_ERROR(Input, "Invalid handle {}", identifier.port); + return Common::Input::PollingError::InvalidHandle; + } + + switch (polling_mode) { + case Common::Input::PollingMode::NFC: + handle->SetNfcMode(); + break; + case Common::Input::PollingMode::Active: + handle->SetActiveMode(); + break; + case Common::Input::PollingMode::Pasive: + handle->SetPasiveMode(); + break; + case Common::Input::PollingMode::Ring: + handle->SetRingConMode(); + break; + default: + return Common::Input::PollingError::NotSupported; + } + + return Common::Input::PollingError::None; +} + +void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, + Joycon::Battery value) { + const auto identifier = GetIdentifier(port, type); + if (value.charging != 0) { + SetBattery(identifier, Common::Input::BatteryLevel::Charging); + return; + } + + Common::Input::BatteryLevel battery{value.status.Value()}; + switch (value.status) { + case 0: + battery = Common::Input::BatteryLevel::Empty; + break; + case 1: + battery = Common::Input::BatteryLevel::Critical; + break; + case 2: + battery = Common::Input::BatteryLevel::Low; + break; + case 3: + battery = Common::Input::BatteryLevel::Medium; + break; + case 4: + default: + battery = Common::Input::BatteryLevel::Full; + break; + } + SetBattery(identifier, battery); +} + +void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type, + const Joycon::Color& value) {} + +void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) { + const auto identifier = GetIdentifier(port, type); + SetButton(identifier, id, value); +} + +void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) { + const auto identifier = GetIdentifier(port, type); + SetAxis(identifier, id, value); +} + +void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id, + const Joycon::MotionData& value) { + const auto identifier = GetIdentifier(port, type); + BasicMotion motion_data{ + .gyro_x = value.gyro_x, + .gyro_y = value.gyro_y, + .gyro_z = value.gyro_z, + .accel_x = value.accel_x, + .accel_y = value.accel_y, + .accel_z = value.accel_z, + .delta_timestamp = 15000, + }; + SetMotion(identifier, id, motion_data); +} + +void Joycons::OnRingConUpdate(f32 ring_data) { + // To simplify ring detection it will always be mapped to an empty identifier for all + // controllers + constexpr PadIdentifier identifier = { + .guid = Common::UUID{}, + .port = 0, + .pad = 0, + }; + SetAxis(identifier, 100, ring_data); +} + +void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) { + const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right); + SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); +} + +std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const { + auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) { + if (!device) { + return false; + } + if (!device->IsConnected()) { + return false; + } + if (device->GetDevicePort() == identifier.port) { + return true; + } + return false; + }; + const auto type = static_cast<Joycon::ControllerType>(identifier.pad); + if (type == Joycon::ControllerType::Left) { + for (const auto& device : left_joycons) { + if (is_handle_active(device)) { + return device; + } + } + } + if (type == Joycon::ControllerType::Right) { + for (const auto& device : right_joycons) { + if (is_handle_active(device)) { + return device; + } + } + } + if (type == Joycon::ControllerType::Pro || type == Joycon::ControllerType::Grip) { + for (const auto& device : pro_joycons) { + if (is_handle_active(device)) { + return device; + } + } + } + return nullptr; +} + +PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const { + return { + .guid = Common::UUID{Common::InvalidUUID}, + .port = port, + .pad = static_cast<std::size_t>(type), + }; +} + +std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { + std::vector<Common::ParamPackage> devices{}; + + auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) { + if (!device) { + return; + } + if (!device->IsConnected()) { + return; + } + std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()), + device->GetDevicePort()); + devices.emplace_back(Common::ParamPackage{ + {"engine", GetEngineName()}, + {"display", std::move(name)}, + {"port", std::to_string(device->GetDevicePort())}, + {"pad", std::to_string(static_cast<std::size_t>(device->GetHandleDeviceType()))}, + }); + }; + + for (const auto& controller : left_joycons) { + add_entry(controller); + } + for (const auto& controller : right_joycons) { + add_entry(controller); + } + for (const auto& controller : pro_joycons) { + add_entry(controller); + } + + return devices; +} + +ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) { + static constexpr std::array<std::pair<Settings::NativeButton::Values, Joycon::PadButton>, 20> + switch_to_joycon_button = { + std::pair{Settings::NativeButton::A, Joycon::PadButton::A}, + {Settings::NativeButton::B, Joycon::PadButton::B}, + {Settings::NativeButton::X, Joycon::PadButton::X}, + {Settings::NativeButton::Y, Joycon::PadButton::Y}, + {Settings::NativeButton::DLeft, Joycon::PadButton::Left}, + {Settings::NativeButton::DUp, Joycon::PadButton::Up}, + {Settings::NativeButton::DRight, Joycon::PadButton::Right}, + {Settings::NativeButton::DDown, Joycon::PadButton::Down}, + {Settings::NativeButton::SL, Joycon::PadButton::LeftSL}, + {Settings::NativeButton::SR, Joycon::PadButton::LeftSR}, + {Settings::NativeButton::L, Joycon::PadButton::L}, + {Settings::NativeButton::R, Joycon::PadButton::R}, + {Settings::NativeButton::ZL, Joycon::PadButton::ZL}, + {Settings::NativeButton::ZR, Joycon::PadButton::ZR}, + {Settings::NativeButton::Plus, Joycon::PadButton::Plus}, + {Settings::NativeButton::Minus, Joycon::PadButton::Minus}, + {Settings::NativeButton::Home, Joycon::PadButton::Home}, + {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture}, + {Settings::NativeButton::LStick, Joycon::PadButton::StickL}, + {Settings::NativeButton::RStick, Joycon::PadButton::StickR}, + }; + + if (!params.Has("port")) { + return {}; + } + + ButtonMapping mapping{}; + for (const auto& [switch_button, joycon_button] : switch_to_joycon_button) { + Common::ParamPackage button_params{}; + button_params.Set("engine", GetEngineName()); + button_params.Set("port", params.Get("port", 0)); + button_params.Set("button", static_cast<int>(joycon_button)); + mapping.insert_or_assign(switch_button, std::move(button_params)); + } + + return mapping; +} + +AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) { + if (!params.Has("port")) { + return {}; + } + + AnalogMapping mapping = {}; + Common::ParamPackage left_analog_params; + left_analog_params.Set("engine", GetEngineName()); + left_analog_params.Set("port", params.Get("port", 0)); + left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX)); + left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY)); + mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); + Common::ParamPackage right_analog_params; + right_analog_params.Set("engine", GetEngineName()); + right_analog_params.Set("port", params.Get("port", 0)); + right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX)); + right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY)); + mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params)); + return mapping; +} + +MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) { + if (!params.Has("port")) { + return {}; + } + + MotionMapping mapping = {}; + Common::ParamPackage left_motion_params; + left_motion_params.Set("engine", GetEngineName()); + left_motion_params.Set("port", params.Get("port", 0)); + left_motion_params.Set("motion", 0); + mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params)); + Common::ParamPackage right_Motion_params; + right_Motion_params.Set("engine", GetEngineName()); + right_Motion_params.Set("port", params.Get("port", 0)); + right_Motion_params.Set("motion", 1); + mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params)); + return mapping; +} + +Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const { + const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0)); + switch (button) { + case Joycon::PadButton::Left: + return Common::Input::ButtonNames::ButtonLeft; + case Joycon::PadButton::Right: + return Common::Input::ButtonNames::ButtonRight; + case Joycon::PadButton::Down: + return Common::Input::ButtonNames::ButtonDown; + case Joycon::PadButton::Up: + return Common::Input::ButtonNames::ButtonUp; + case Joycon::PadButton::LeftSL: + case Joycon::PadButton::RightSL: + return Common::Input::ButtonNames::TriggerSL; + case Joycon::PadButton::LeftSR: + case Joycon::PadButton::RightSR: + return Common::Input::ButtonNames::TriggerSR; + case Joycon::PadButton::L: + return Common::Input::ButtonNames::TriggerL; + case Joycon::PadButton::R: + return Common::Input::ButtonNames::TriggerR; + case Joycon::PadButton::ZL: + return Common::Input::ButtonNames::TriggerZL; + case Joycon::PadButton::ZR: + return Common::Input::ButtonNames::TriggerZR; + case Joycon::PadButton::A: + return Common::Input::ButtonNames::ButtonA; + case Joycon::PadButton::B: + return Common::Input::ButtonNames::ButtonB; + case Joycon::PadButton::X: + return Common::Input::ButtonNames::ButtonX; + case Joycon::PadButton::Y: + return Common::Input::ButtonNames::ButtonY; + case Joycon::PadButton::Plus: + return Common::Input::ButtonNames::ButtonPlus; + case Joycon::PadButton::Minus: + return Common::Input::ButtonNames::ButtonMinus; + case Joycon::PadButton::Home: + return Common::Input::ButtonNames::ButtonHome; + case Joycon::PadButton::Capture: + return Common::Input::ButtonNames::ButtonCapture; + case Joycon::PadButton::StickL: + return Common::Input::ButtonNames::ButtonStickL; + case Joycon::PadButton::StickR: + return Common::Input::ButtonNames::ButtonStickR; + default: + return Common::Input::ButtonNames::Undefined; + } +} + +Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const { + if (params.Has("button")) { + return GetUIButtonName(params); + } + if (params.Has("axis")) { + return Common::Input::ButtonNames::Value; + } + if (params.Has("motion")) { + return Common::Input::ButtonNames::Engine; + } + + return Common::Input::ButtonNames::Invalid; +} + +std::string Joycons::JoyconName(Joycon::ControllerType type) const { + switch (type) { + case Joycon::ControllerType::Left: + return "Left Joycon"; + case Joycon::ControllerType::Right: + return "Right Joycon"; + case Joycon::ControllerType::Pro: + return "Pro Controller"; + case Joycon::ControllerType::Grip: + return "Grip Controller"; + default: + return "Unknow Joycon"; + } +} +} // namespace InputCommon -- cgit v1.2.3-70-g09d2 From 2d802893e706c4ce7fd6f320db0eed2bf90b2d45 Mon Sep 17 00:00:00 2001 From: Narr the Reg <juangerman-13@hotmail.com> Date: Tue, 20 Dec 2022 12:45:54 -0600 Subject: input_common: Disable SDL driver with switch controllers --- src/common/settings.h | 1 + src/input_common/drivers/joycon.cpp | 4 ++++ src/input_common/drivers/sdl_driver.cpp | 19 ++++++++++++++++--- src/yuzu/configuration/config.cpp | 2 ++ .../configuration/configure_input_advanced.cpp | 2 ++ src/yuzu/configuration/configure_input_advanced.ui | 22 +++++++++++++++++++--- 6 files changed, 44 insertions(+), 6 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/common/settings.h b/src/common/settings.h index 80b2eeabcd..4b4da4da29 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -483,6 +483,7 @@ struct Values { Setting<bool> enable_raw_input{false, "enable_raw_input"}; Setting<bool> controller_navigation{true, "controller_navigation"}; + Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"}; SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index eab10d11c0..1fca11d34e 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -13,6 +13,10 @@ namespace InputCommon { Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) { + // Avoid conflicting with SDL driver + if (!Settings::values.enable_joycon_driver) { + return; + } LOG_INFO(Input, "Joycon driver Initialization started"); const int init_res = SDL_hid_init(); if (init_res == 0) { diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 4818bb7442..c9496a0d86 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -318,6 +318,14 @@ void SDLDriver::InitJoystick(int joystick_index) { const auto guid = GetGUID(sdl_joystick); + if (Settings::values.enable_joycon_driver) { + if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e) { + LOG_ERROR(Input, "Device black listed {}", joystick_index); + SDL_JoystickClose(sdl_joystick); + return; + } + } + std::scoped_lock lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); @@ -440,9 +448,14 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); - // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and - // not a generic one - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled + if (Settings::values.enable_joycon_driver) { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); + } else { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); + } // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native // driver on Linux. diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 0db62baa32..d8b26ebd88 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -440,6 +440,7 @@ void Config::ReadControlValues() { ReadBasicSetting(Settings::values.emulate_analog_keyboard); Settings::values.mouse_panning = false; ReadBasicSetting(Settings::values.mouse_panning_sensitivity); + ReadBasicSetting(Settings::values.enable_joycon_driver); ReadBasicSetting(Settings::values.tas_enable); ReadBasicSetting(Settings::values.tas_loop); @@ -1139,6 +1140,7 @@ void Config::SaveControlValues() { WriteGlobalSetting(Settings::values.enable_accurate_vibrations); WriteGlobalSetting(Settings::values.motion_enabled); WriteBasicSetting(Settings::values.enable_raw_input); + WriteBasicSetting(Settings::values.enable_joycon_driver); WriteBasicSetting(Settings::values.keyboard_enabled); WriteBasicSetting(Settings::values.emulate_analog_keyboard); WriteBasicSetting(Settings::values.mouse_panning_sensitivity); diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 235b813d97..77b976e740 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -138,6 +138,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { Settings::values.controller_navigation = ui->controller_navigation->isChecked(); Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); + Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked(); } void ConfigureInputAdvanced::LoadConfiguration() { @@ -172,6 +173,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); + ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue()); UpdateUIEnabled(); } diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index fac8cf827d..75d96d3ab5 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -2696,6 +2696,22 @@ </widget> </item> <item row="5" column="0"> + <widget class="QCheckBox" name="enable_joycon_driver"> + <property name="toolTip"> + <string>Requires restarting yuzu</string> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>23</height> + </size> + </property> + <property name="text"> + <string>Enable direct JoyCon driver</string> + </property> + </widget> + </item> + <item row="6" column="0"> <widget class="QCheckBox" name="mouse_panning"> <property name="minimumSize"> <size> @@ -2708,7 +2724,7 @@ </property> </widget> </item> - <item row="5" column="2"> + <item row="6" column="2"> <widget class="QSpinBox" name="mouse_panning_sensitivity"> <property name="toolTip"> <string>Mouse sensitivity</string> @@ -2730,14 +2746,14 @@ </property> </widget> </item> - <item row="6" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="motion_touch"> <property name="text"> <string>Motion / Touch</string> </property> </widget> </item> - <item row="6" column="2"> + <item row="7" column="2"> <widget class="QPushButton" name="buttonMotionTouch"> <property name="text"> <string>Configure</string> -- cgit v1.2.3-70-g09d2 From ed5fa10e9729cf55205533f62a428e646aa5ed7c Mon Sep 17 00:00:00 2001 From: Narr the Reg <juangerman-13@hotmail.com> Date: Tue, 20 Dec 2022 13:23:31 -0600 Subject: core: hid: Enable pulling color data from controllers --- src/core/hid/emulated_controller.cpp | 80 ++++++++++++++++++++++++++++++++++++ src/core/hid/emulated_controller.h | 12 ++++++ src/core/hid/input_converter.cpp | 14 +++++++ src/core/hid/input_converter.h | 10 ++++- src/input_common/drivers/joycon.cpp | 11 ++++- src/input_common/input_engine.cpp | 37 +++++++++++++++++ src/input_common/input_engine.h | 6 +++ src/input_common/input_poller.cpp | 67 ++++++++++++++++++++++++++++++ src/input_common/input_poller.h | 11 +++++ 9 files changed, 246 insertions(+), 2 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 128101e8ca..89638cb856 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -93,6 +93,7 @@ void EmulatedController::ReloadFromSettings() { motion_params[index] = Common::ParamPackage(player.motions[index]); } + controller.color_values = {}; controller.colors_state.fullkey = { .body = GetNpadColor(player.body_color_left), .button = GetNpadColor(player.button_color_left), @@ -132,6 +133,11 @@ void EmulatedController::LoadDevices() { trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; + color_params[LeftIndex] = left_joycon; + color_params[RightIndex] = right_joycon; + color_params[LeftIndex].Set("color", true); + color_params[RightIndex].Set("color", true); + battery_params[LeftIndex] = left_joycon; battery_params[RightIndex] = right_joycon; battery_params[LeftIndex].Set("battery", true); @@ -160,6 +166,7 @@ void EmulatedController::LoadDevices() { Common::Input::CreateInputDevice); std::ranges::transform(battery_params, battery_devices.begin(), Common::Input::CreateInputDevice); + std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice); camera_devices = Common::Input::CreateInputDevice(camera_params); ring_analog_device = Common::Input::CreateInputDevice(ring_params); nfc_devices = Common::Input::CreateInputDevice(nfc_params); @@ -324,6 +331,19 @@ void EmulatedController::ReloadInput() { battery_devices[index]->ForceUpdate(); } + for (std::size_t index = 0; index < color_devices.size(); ++index) { + if (!color_devices[index]) { + continue; + } + color_devices[index]->SetCallback({ + .on_change = + [this, index](const Common::Input::CallbackStatus& callback) { + SetColors(callback, index); + }, + }); + color_devices[index]->ForceUpdate(); + } + for (std::size_t index = 0; index < motion_devices.size(); ++index) { if (!motion_devices[index]) { continue; @@ -429,6 +449,9 @@ void EmulatedController::UnloadInput() { for (auto& battery : battery_devices) { battery.reset(); } + for (auto& color : color_devices) { + color.reset(); + } for (auto& output : output_devices) { output.reset(); } @@ -458,6 +481,11 @@ void EmulatedController::EnableConfiguration() { void EmulatedController::DisableConfiguration() { is_configuring = false; + // Get Joycon colors before turning on the controller + for (const auto& color_device : color_devices) { + color_device->ForceUpdate(); + } + // Apply temporary npad type to the real controller if (tmp_npad_type != npad_type) { if (is_connected) { @@ -926,6 +954,58 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback TriggerOnChange(ControllerTriggerType::Motion, true); } +void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback, + std::size_t index) { + if (index >= controller.color_values.size()) { + return; + } + std::unique_lock lock{mutex}; + controller.color_values[index] = TransformToColor(callback); + + if (is_configuring) { + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Color, false); + return; + } + + if (controller.color_values[index].body == 0) { + return; + } + + controller.colors_state.fullkey = { + .body = GetNpadColor(controller.color_values[index].body), + .button = GetNpadColor(controller.color_values[index].buttons), + }; + if (npad_type == NpadStyleIndex::ProController) { + controller.colors_state.left = { + .body = GetNpadColor(controller.color_values[index].left_grip), + .button = GetNpadColor(controller.color_values[index].buttons), + }; + controller.colors_state.right = { + .body = GetNpadColor(controller.color_values[index].right_grip), + .button = GetNpadColor(controller.color_values[index].buttons), + }; + } else { + switch (index) { + case LeftIndex: + controller.colors_state.left = { + .body = GetNpadColor(controller.color_values[index].body), + .button = GetNpadColor(controller.color_values[index].buttons), + }; + break; + case RightIndex: + controller.colors_state.right = { + .body = GetNpadColor(controller.color_values[index].body), + .button = GetNpadColor(controller.color_values[index].buttons), + }; + break; + } + } + + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Color, true); +} + void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index) { if (index >= controller.battery_values.size()) { diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index aed331a1aa..d044cc36b8 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -35,6 +35,8 @@ using ControllerMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; using TriggerDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; +using ColorDevices = + std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; using BatteryDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; @@ -46,6 +48,7 @@ using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::Nu using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; +using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>; using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; using CameraParams = Common::ParamPackage; using RingAnalogParams = Common::ParamPackage; @@ -457,6 +460,13 @@ private: */ void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); + /** + * Updates the color status of the controller + * @param callback A CallbackStatus containing the color status + * @param index color ID of the to be updated + */ + void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index); + /** * Updates the battery status of the controller * @param callback A CallbackStatus containing the battery status @@ -515,6 +525,7 @@ private: ControllerMotionParams motion_params; TriggerParams trigger_params; BatteryParams battery_params; + ColorParams color_params; CameraParams camera_params; RingAnalogParams ring_params; NfcParams nfc_params; @@ -525,6 +536,7 @@ private: ControllerMotionDevices motion_devices; TriggerDevices trigger_devices; BatteryDevices battery_devices; + ColorDevices color_devices; CameraDevices camera_devices; RingAnalogDevice ring_analog_device; NfcDevices nfc_devices; diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 5026928753..d7e253044a 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -304,6 +304,20 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal return nfc; } +Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) { + Common::Input::BodyColorStatus color{}; + switch (callback.type) { + case Common::Input::InputType::Color: + color = callback.color_status; + break; + default: + LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type); + break; + } + + return color; +} + void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { const auto& properties = analog.properties; float& raw_value = analog.raw_value; diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h index b7eb6e660c..c51c03e57e 100644 --- a/src/core/hid/input_converter.h +++ b/src/core/hid/input_converter.h @@ -88,10 +88,18 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu * Converts raw input data into a valid nfc status. * * @param callback Supported callbacks: Nfc. - * @return A valid CameraObject object. + * @return A valid data tag vector. */ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); +/** + * Converts raw input data into a valid color status. + * + * @param callback Supported callbacks: Color. + * @return A valid Color object. + */ +Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback); + /** * Converts raw analog data into a valid analog value * @param analog An analog object containing raw data and properties diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 1fca11d34e..c6f78c9893 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -335,7 +335,16 @@ void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, } void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type, - const Joycon::Color& value) {} + const Joycon::Color& value) { + const auto identifier = GetIdentifier(port, type); + Common::Input::BodyColorStatus color{ + .body = value.body, + .buttons = value.buttons, + .left_grip = value.left_grip, + .right_grip = value.right_grip, + }; + SetColor(identifier, color); +} void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) { const auto identifier = GetIdentifier(port, type); diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 61cfd0911c..91aa96aa73 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp @@ -79,6 +79,17 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat TriggerOnBatteryChange(identifier, value); } +void InputEngine::SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value) { + { + std::scoped_lock lock{mutex}; + ControllerData& controller = controller_list.at(identifier); + if (!configuring) { + controller.color = value; + } + } + TriggerOnColorChange(identifier, value); +} + void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { { std::scoped_lock lock{mutex}; @@ -176,6 +187,18 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif return controller.battery; } +Common::Input::BodyColorStatus InputEngine::GetColor(const PadIdentifier& identifier) const { + std::scoped_lock lock{mutex}; + const auto controller_iter = controller_list.find(identifier); + if (controller_iter == controller_list.cend()) { + LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), + identifier.pad, identifier.port); + return {}; + } + const ControllerData& controller = controller_iter->second; + return controller.color; +} + BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); @@ -328,6 +351,20 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, } } +void InputEngine::TriggerOnColorChange(const PadIdentifier& identifier, + [[maybe_unused]] Common::Input::BodyColorStatus value) { + std::scoped_lock lock{mutex_callback}; + for (const auto& poller_pair : callback_list) { + const InputIdentifier& poller = poller_pair.second; + if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Color, 0)) { + continue; + } + if (poller.callback.on_change) { + poller.callback.on_change(); + } + } +} + void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, const BasicMotion& value) { std::scoped_lock lock{mutex_callback}; diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 6cbcf5207e..6301c5719a 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -40,6 +40,7 @@ enum class EngineInputType { Battery, Button, Camera, + Color, HatButton, Motion, Nfc, @@ -199,6 +200,7 @@ public: bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; f32 GetAxis(const PadIdentifier& identifier, int axis) const; Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; + Common::Input::BodyColorStatus GetColor(const PadIdentifier& identifier) const; BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; @@ -212,6 +214,7 @@ protected: void SetHatButton(const PadIdentifier& identifier, int button, u8 value); void SetAxis(const PadIdentifier& identifier, int axis, f32 value); void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); + void SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value); void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); @@ -227,6 +230,7 @@ private: std::unordered_map<int, float> axes; std::unordered_map<int, BasicMotion> motions; Common::Input::BatteryLevel battery{}; + Common::Input::BodyColorStatus color{}; Common::Input::CameraStatus camera{}; Common::Input::NfcStatus nfc{}; }; @@ -235,6 +239,8 @@ private: void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); + void TriggerOnColorChange(const PadIdentifier& identifier, + Common::Input::BodyColorStatus value); void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, const BasicMotion& value); void TriggerOnCameraChange(const PadIdentifier& identifier, diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index fb8be42e2e..368ffbdd5a 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -498,6 +498,58 @@ private: InputEngine* input_engine; }; +class InputFromColor final : public Common::Input::InputDevice { +public: + explicit InputFromColor(PadIdentifier identifier_, InputEngine* input_engine_) + : identifier(identifier_), input_engine(input_engine_) { + UpdateCallback engine_callback{[this]() { OnChange(); }}; + const InputIdentifier input_identifier{ + .identifier = identifier, + .type = EngineInputType::Color, + .index = 0, + .callback = engine_callback, + }; + last_color_value = {}; + callback_key = input_engine->SetCallback(input_identifier); + } + + ~InputFromColor() override { + input_engine->DeleteCallback(callback_key); + } + + Common::Input::BodyColorStatus GetStatus() const { + return input_engine->GetColor(identifier); + } + + void ForceUpdate() override { + const Common::Input::CallbackStatus status{ + .type = Common::Input::InputType::Color, + .color_status = GetStatus(), + }; + + last_color_value = status.color_status; + TriggerOnChange(status); + } + + void OnChange() { + const Common::Input::CallbackStatus status{ + .type = Common::Input::InputType::Color, + .color_status = GetStatus(), + }; + + if (status.color_status.body != last_color_value.body) { + last_color_value = status.color_status; + TriggerOnChange(status); + } + } + +private: + const PadIdentifier identifier; + int callback_key; + Common::Input::BodyColorStatus last_color_value; + InputEngine* input_engine; +}; + class InputFromMotion final : public Common::Input::InputDevice { public: explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_, @@ -966,6 +1018,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice( return std::make_unique<InputFromBattery>(identifier, input_engine.get()); } +std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateColorDevice( + const Common::ParamPackage& params) { + const PadIdentifier identifier = { + .guid = Common::UUID{params.Get("guid", "")}, + .port = static_cast<std::size_t>(params.Get("port", 0)), + .pad = static_cast<std::size_t>(params.Get("pad", 0)), + }; + + input_engine->PreSetController(identifier); + return std::make_unique<InputFromColor>(identifier, input_engine.get()); +} + std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( Common::ParamPackage params) { const PadIdentifier identifier = { @@ -1053,6 +1117,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create( if (params.Has("battery")) { return CreateBatteryDevice(params); } + if (params.Has("color")) { + return CreateColorDevice(params); + } if (params.Has("camera")) { return CreateCameraDevice(params); } diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h index d7db13ce42..e097e254c8 100644 --- a/src/input_common/input_poller.h +++ b/src/input_common/input_poller.h @@ -190,6 +190,17 @@ private: std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice( const Common::ParamPackage& params); + /** + * Creates a color device from the parameters given. + * @param params contains parameters for creating the device: + * - "guid": text string for identifying controllers + * - "port": port of the connected device + * - "pad": slot of the connected controller + * @returns a unique input device with the parameters specified + */ + std::unique_ptr<Common::Input::InputDevice> CreateColorDevice( + const Common::ParamPackage& params); + /** * Creates a motion device from the parameters given. * @param params contains parameters for creating the device: -- cgit v1.2.3-70-g09d2 From f09a023292e659af46d551b9b134d94d000a57c7 Mon Sep 17 00:00:00 2001 From: Narr the Reg <juangerman-13@hotmail.com> Date: Tue, 20 Dec 2022 20:27:34 -0600 Subject: input_common: Add support for joycon input reports --- src/input_common/CMakeLists.txt | 4 + src/input_common/drivers/joycon.cpp | 47 +-- src/input_common/helpers/joycon_driver.cpp | 100 +++---- src/input_common/helpers/joycon_driver.h | 23 +- .../helpers/joycon_protocol/poller.cpp | 315 +++++++++++++++++++++ src/input_common/helpers/joycon_protocol/poller.h | 77 +++++ .../helpers/joycon_protocol/rumble.cpp | 299 +++++++++++++++++++ src/input_common/helpers/joycon_protocol/rumble.h | 33 +++ 8 files changed, 798 insertions(+), 100 deletions(-) create mode 100644 src/input_common/helpers/joycon_protocol/poller.cpp create mode 100644 src/input_common/helpers/joycon_protocol/poller.h create mode 100644 src/input_common/helpers/joycon_protocol/rumble.cpp create mode 100644 src/input_common/helpers/joycon_protocol/rumble.h (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index d4307351c9..4ab1ccbfbe 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -64,6 +64,10 @@ if (ENABLE_SDL2) helpers/joycon_protocol/generic_functions.cpp helpers/joycon_protocol/generic_functions.h helpers/joycon_protocol/joycon_types.h + helpers/joycon_protocol/poller.cpp + helpers/joycon_protocol/poller.h + helpers/joycon_protocol/rumble.cpp + helpers/joycon_protocol/rumble.h ) target_link_libraries(input_common PRIVATE SDL2::SDL2) target_compile_definitions(input_common PRIVATE HAVE_SDL2) diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index c6f78c9893..dbe730e1a3 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -167,30 +167,31 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) { if (result == Joycon::DriverResult::Success) { LOG_WARNING(Input, "Initialize device"); - std::function<void(Joycon::Battery)> on_battery_data; - std::function<void(Joycon::Color)> on_button_data; - std::function<void(int, f32)> on_stick_data; - std::function<void(int, std::array<u8, 6>)> on_motion_data; - std::function<void(s16)> on_ring_data; - std::function<void(const std::vector<u8>&)> on_amiibo_data; - const std::size_t port = handle->GetDevicePort(); - handle->on_battery_data = { - [this, port, type](Joycon::Battery value) { OnBatteryUpdate(port, type, value); }}; - handle->on_color_data = { - [this, port, type](Joycon::Color value) { OnColorUpdate(port, type, value); }}; - handle->on_button_data = { - [this, port, type](int id, bool value) { OnButtonUpdate(port, type, id, value); }}; - handle->on_stick_data = { - [this, port, type](int id, f32 value) { OnStickUpdate(port, type, id, value); }}; - handle->on_motion_data = {[this, port, type](int id, Joycon::MotionData value) { - OnMotionUpdate(port, type, id, value); - }}; - handle->on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}; - handle->on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) { - OnAmiiboUpdate(port, amiibo_data); - }}; + const Joycon::JoyconCallbacks callbacks{ + .on_battery_data = {[this, port, type](Joycon::Battery value) { + OnBatteryUpdate(port, type, value); + }}, + .on_color_data = {[this, port, type](Joycon::Color value) { + OnColorUpdate(port, type, value); + }}, + .on_button_data = {[this, port, type](int id, bool value) { + OnButtonUpdate(port, type, id, value); + }}, + .on_stick_data = {[this, port, type](int id, f32 value) { + OnStickUpdate(port, type, id, value); + }}, + .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) { + OnMotionUpdate(port, type, id, value); + }}, + .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, + .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) { + OnAmiiboUpdate(port, amiibo_data); + }}, + }; + handle->InitializeDevice(); + handle->SetCallbacks(callbacks); } } @@ -235,7 +236,7 @@ Common::Input::VibrationError Joycons::SetVibration( .low_amplitude = vibration.low_amplitude, .low_frequency = vibration.low_frequency, .high_amplitude = vibration.high_amplitude, - .high_frequency = vibration.high_amplitude, + .high_frequency = vibration.high_frequency, }; auto handle = GetHandle(identifier); if (handle == nullptr) { diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index ac11be1c17..5d0aeabf5f 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -66,6 +66,7 @@ DriverResult JoyconDriver::InitializeDevice() { // Initialize HW Protocols calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle); generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); + rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); // Get fixed joycon info generic_protocol->GetVersionNumber(version); @@ -90,6 +91,10 @@ DriverResult JoyconDriver::InitializeDevice() { // Apply HW configuration SetPollingMode(); + // Initialize joycon poller + joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, + right_stick_calibration, motion_calibration); + // Start pooling for data is_connected = true; if (!input_thread_running) { @@ -142,15 +147,40 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { void JoyconDriver::OnNewData(std::span<u8> buffer) { const auto report_mode = static_cast<InputReport>(buffer[0]); + // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion + // experience + switch (report_mode) { + case InputReport::STANDARD_FULL_60HZ: + case InputReport::NFC_IR_MODE_60HZ: + case InputReport::SIMPLE_HID_MODE: { + const auto now = std::chrono::steady_clock::now(); + const auto new_delta_time = static_cast<u64>( + std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); + delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10; + last_update = now; + joycon_poller->UpdateColor(color); + break; + } + default: + break; + } + + const MotionStatus motion_status{ + .is_enabled = motion_enabled, + .delta_time = delta_time, + .gyro_sensitivity = gyro_sensitivity, + .accelerometer_sensitivity = accelerometer_sensitivity, + }; + switch (report_mode) { case InputReport::STANDARD_FULL_60HZ: - ReadActiveMode(buffer); + joycon_poller->ReadActiveMode(buffer, motion_status); break; case InputReport::NFC_IR_MODE_60HZ: - ReadNfcIRMode(buffer); + joycon_poller->ReadNfcIRMode(buffer, motion_status); break; case InputReport::SIMPLE_HID_MODE: - ReadPassiveMode(buffer); + joycon_poller->ReadPassiveMode(buffer); break; case InputReport::SUBCMD_REPLY: LOG_DEBUG(Input, "Unhandled command reply"); @@ -164,6 +194,8 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { void JoyconDriver::SetPollingMode() { disable_input_thread = true; + rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); + if (motion_enabled && supported_features.motion) { generic_protocol->EnableImu(true); generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance, @@ -209,62 +241,6 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() { return features; } -void JoyconDriver::ReadActiveMode(std::span<u8> buffer) { - InputReportActive data{}; - memcpy(&data, buffer.data(), sizeof(InputReportActive)); - - // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion - // experience - const auto now = std::chrono::steady_clock::now(); - const auto new_delta_time = - std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count(); - delta_time = static_cast<u64>((delta_time * 0.8f) + (new_delta_time * 0.2)); - last_update = now; - - switch (device_type) { - case Joycon::ControllerType::Left: - break; - case Joycon::ControllerType::Right: - break; - case Joycon::ControllerType::Pro: - break; - case Joycon::ControllerType::Grip: - case Joycon::ControllerType::Dual: - case Joycon::ControllerType::None: - break; - } - - on_battery_data(data.battery_status); - on_color_data(color); -} - -void JoyconDriver::ReadPassiveMode(std::span<u8> buffer) { - InputReportPassive data{}; - memcpy(&data, buffer.data(), sizeof(InputReportPassive)); - - switch (device_type) { - case Joycon::ControllerType::Left: - break; - case Joycon::ControllerType::Right: - break; - case Joycon::ControllerType::Pro: - break; - case Joycon::ControllerType::Grip: - case Joycon::ControllerType::Dual: - case Joycon::ControllerType::None: - break; - } -} - -void JoyconDriver::ReadNfcIRMode(std::span<u8> buffer) { - // This mode is compatible with the active mode - ReadActiveMode(buffer); - - if (!nfc_enabled) { - return; - } -} - bool JoyconDriver::IsInputThreadValid() const { if (!is_connected) { return false; @@ -302,7 +278,7 @@ DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { if (disable_input_thread) { return DriverResult::HandleInUse; } - return DriverResult::NotSupported; + return rumble_protocol->SendVibration(vibration); } DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { @@ -398,6 +374,10 @@ SerialNumber JoyconDriver::GetHandleSerialNumber() const { return handle_serial_number; } +void JoyconDriver::SetCallbacks(const Joycon::JoyconCallbacks& callbacks) { + joycon_poller->SetCallbacks(callbacks); +} + Joycon::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, ControllerType& controller_type) { std::array<std::pair<u32, Joycon::ControllerType>, 4> supported_devices{ diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index 275c97b913..48ba859f4e 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -11,6 +11,8 @@ #include "input_common/helpers/joycon_protocol/calibration.h" #include "input_common/helpers/joycon_protocol/generic_functions.h" #include "input_common/helpers/joycon_protocol/joycon_types.h" +#include "input_common/helpers/joycon_protocol/poller.h" +#include "input_common/helpers/joycon_protocol/rumble.h" namespace InputCommon::Joycon { @@ -42,6 +44,8 @@ public: DriverResult SetNfcMode(); DriverResult SetRingConMode(); + void SetCallbacks(const Joycon::JoyconCallbacks& callbacks); + // Returns device type from hidapi handle static Joycon::DriverResult GetDeviceType(SDL_hid_device_info* device_info, Joycon::ControllerType& controller_type); @@ -50,14 +54,6 @@ public: static Joycon::DriverResult GetSerialNumber(SDL_hid_device_info* device_info, Joycon::SerialNumber& serial_number); - std::function<void(Battery)> on_battery_data; - std::function<void(Color)> on_color_data; - std::function<void(int, bool)> on_button_data; - std::function<void(int, f32)> on_stick_data; - std::function<void(int, MotionData)> on_motion_data; - std::function<void(f32)> on_ring_data; - std::function<void(const std::vector<u8>&)> on_amiibo_data; - private: struct SupportedFeatures { bool passive{}; @@ -86,18 +82,11 @@ private: /// Returns a list of supported features that can be enabled on this device SupportedFeatures GetSupportedFeatures(); - /// Handles data from passive packages - void ReadPassiveMode(std::span<u8> buffer); - - /// Handles data from active packages - void ReadActiveMode(std::span<u8> buffer); - - /// Handles data from nfc or ir packages - void ReadNfcIRMode(std::span<u8> buffer); - // Protocol Features std::unique_ptr<CalibrationProtocol> calibration_protocol = nullptr; std::unique_ptr<GenericProtocol> generic_protocol = nullptr; + std::unique_ptr<JoyconPoller> joycon_poller = nullptr; + std::unique_ptr<RumbleProtocol> rumble_protocol = nullptr; // Connection status bool is_connected{}; diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp new file mode 100644 index 0000000000..341479c0ca --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -0,0 +1,315 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "input_common/helpers/joycon_protocol/poller.h" + +namespace InputCommon::Joycon { + +JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_, + JoyStickCalibration right_stick_calibration_, + MotionCalibration motion_calibration_) + : device_type{device_type_}, left_stick_calibration{left_stick_calibration_}, + right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {} + +void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) { + callbacks = std::move(callbacks_); +} + +void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status) { + InputReportActive data{}; + memcpy(&data, buffer.data(), sizeof(InputReportActive)); + + switch (device_type) { + case Joycon::ControllerType::Left: + UpdateActiveLeftPadInput(data, motion_status); + break; + case Joycon::ControllerType::Right: + UpdateActiveRightPadInput(data, motion_status); + break; + case Joycon::ControllerType::Pro: + UpdateActiveProPadInput(data, motion_status); + break; + case Joycon::ControllerType::Grip: + case Joycon::ControllerType::Dual: + case Joycon::ControllerType::None: + break; + } + + callbacks.on_battery_data(data.battery_status); +} + +void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) { + InputReportPassive data{}; + memcpy(&data, buffer.data(), sizeof(InputReportPassive)); + + switch (device_type) { + case Joycon::ControllerType::Left: + UpdatePasiveLeftPadInput(data); + break; + case Joycon::ControllerType::Right: + UpdatePasiveRightPadInput(data); + break; + case Joycon::ControllerType::Pro: + UpdatePasiveProPadInput(data); + break; + case Joycon::ControllerType::Grip: + case Joycon::ControllerType::Dual: + case Joycon::ControllerType::None: + break; + } +} + +void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) { + // This mode is compatible with the active mode + ReadActiveMode(buffer, motion_status); +} + +void JoyconPoller::UpdateColor(const Color& color) { + callbacks.on_color_data(color); +} + +void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input, + const MotionStatus& motion_status) { + static constexpr std::array<Joycon::PadButton, 11> left_buttons{ + Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right, + Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR, + Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus, + Joycon::PadButton::Capture, Joycon::PadButton::StickL, + }; + + const u32 raw_button = + static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16)); + for (std::size_t i = 0; i < left_buttons.size(); ++i) { + const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0; + const int button = static_cast<int>(left_buttons[i]); + callbacks.on_button_data(button, button_status); + } + + const u16 raw_left_axis_x = + static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8)); + const u16 raw_left_axis_y = + static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4)); + const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x); + const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y); + callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x); + callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y); + + if (motion_status.is_enabled) { + auto left_motion = GetMotionInput(input, motion_status); + // Rotate motion axis to the correct direction + left_motion.accel_y = -left_motion.accel_y; + left_motion.accel_z = -left_motion.accel_z; + left_motion.gyro_x = -left_motion.gyro_x; + callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion); + } +} + +void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input, + const MotionStatus& motion_status) { + static constexpr std::array<Joycon::PadButton, 11> right_buttons{ + Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B, + Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR, + Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus, + Joycon::PadButton::Home, Joycon::PadButton::StickR, + }; + + const u32 raw_button = + static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16)); + for (std::size_t i = 0; i < right_buttons.size(); ++i) { + const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0; + const int button = static_cast<int>(right_buttons[i]); + callbacks.on_button_data(button, button_status); + } + + const u16 raw_right_axis_x = + static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8)); + const u16 raw_right_axis_y = + static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4)); + const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x); + const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y); + callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x); + callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y); + + if (motion_status.is_enabled) { + auto right_motion = GetMotionInput(input, motion_status); + // Rotate motion axis to the correct direction + right_motion.accel_x = -right_motion.accel_x; + right_motion.accel_y = -right_motion.accel_y; + right_motion.gyro_z = -right_motion.gyro_z; + callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion); + } +} + +void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input, + const MotionStatus& motion_status) { + static constexpr std::array<Joycon::PadButton, 18> pro_buttons{ + Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right, + Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL, + Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y, + Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A, + Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus, + Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR, + }; + + const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) | + (input.button_input[1] << 16)); + for (std::size_t i = 0; i < pro_buttons.size(); ++i) { + const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0; + const int button = static_cast<int>(pro_buttons[i]); + callbacks.on_button_data(button, button_status); + } + + const u16 raw_left_axis_x = + static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8)); + const u16 raw_left_axis_y = + static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4)); + const u16 raw_right_axis_x = + static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8)); + const u16 raw_right_axis_y = + static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4)); + + const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x); + const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y); + const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x); + const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y); + callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x); + callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y); + callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x); + callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y); + + if (motion_status.is_enabled) { + auto pro_motion = GetMotionInput(input, motion_status); + pro_motion.gyro_x = -pro_motion.gyro_x; + pro_motion.accel_y = -pro_motion.accel_y; + pro_motion.accel_z = -pro_motion.accel_z; + callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion); + callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion); + } +} + +void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) { + static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{ + Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, + Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, + Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, + Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, + Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture, + Joycon::PasivePadButton::StickL, + }; + + for (std::size_t i = 0; i < left_buttons.size(); ++i) { + const bool button_status = (input.button_input & static_cast<u32>(left_buttons[i])) != 0; + const int button = static_cast<int>(left_buttons[i]); + callbacks.on_button_data(button, button_status); + } +} + +void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) { + static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{ + Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, + Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, + Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, + Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, + Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home, + Joycon::PasivePadButton::StickR, + }; + + for (std::size_t i = 0; i < right_buttons.size(); ++i) { + const bool button_status = (input.button_input & static_cast<u32>(right_buttons[i])) != 0; + const int button = static_cast<int>(right_buttons[i]); + callbacks.on_button_data(button, button_status); + } +} + +void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) { + static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{ + Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, + Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, + Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, + Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, + Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus, + Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home, + Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR, + }; + + for (std::size_t i = 0; i < pro_buttons.size(); ++i) { + const bool button_status = (input.button_input & static_cast<u32>(pro_buttons[i])) != 0; + const int button = static_cast<int>(pro_buttons[i]); + callbacks.on_button_data(button, button_status); + } +} + +f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const { + const f32 value = static_cast<f32>(raw_value - calibration.center); + if (value > 0.0f) { + return value / calibration.max; + } + return value / calibration.min; +} + +f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal, + AccelerometerSensitivity sensitivity) const { + const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4; + switch (sensitivity) { + case Joycon::AccelerometerSensitivity::G2: + return value / 4.0f; + case Joycon::AccelerometerSensitivity::G4: + return value / 2.0f; + case Joycon::AccelerometerSensitivity::G8: + return value; + case Joycon::AccelerometerSensitivity::G16: + return value * 2.0f; + } + return value; +} + +f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal, + GyroSensitivity sensitivity) const { + const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f; + switch (sensitivity) { + case Joycon::GyroSensitivity::DPS250: + return value / 8.0f; + case Joycon::GyroSensitivity::DPS500: + return value / 4.0f; + case Joycon::GyroSensitivity::DPS1000: + return value / 2.0f; + case Joycon::GyroSensitivity::DPS2000: + return value; + } + return value; +} + +s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis, + const InputReportActive& input) const { + return input.motion_input[(sensor * 3) + axis]; +} + +MotionData JoyconPoller::GetMotionInput(const InputReportActive& input, + const MotionStatus& motion_status) const { + MotionData motion{}; + const auto& accel_cal = motion_calibration.accelerometer; + const auto& gyro_cal = motion_calibration.gyro; + const s16 raw_accel_x = input.motion_input[1]; + const s16 raw_accel_y = input.motion_input[0]; + const s16 raw_accel_z = input.motion_input[2]; + const s16 raw_gyro_x = input.motion_input[4]; + const s16 raw_gyro_y = input.motion_input[3]; + const s16 raw_gyro_z = input.motion_input[5]; + + motion.delta_timestamp = motion_status.delta_time; + motion.accel_x = + GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity); + motion.accel_y = + GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity); + motion.accel_z = + GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity); + motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity); + motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity); + motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity); + + // TODO(German77): Return all three samples data + return motion; +} + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h new file mode 100644 index 0000000000..fff681d0a1 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/poller.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse +// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c +// https://github.com/CTCaer/jc_toolkit +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + +#pragma once + +#include <functional> +#include <span> + +#include "input_common/helpers/joycon_protocol/joycon_types.h" + +namespace InputCommon::Joycon { + +// Handles input packages and triggers the corresponding input events +class JoyconPoller { +public: + JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_, + JoyStickCalibration right_stick_calibration_, + MotionCalibration motion_calibration_); + + void SetCallbacks(const Joycon::JoyconCallbacks& callbacks_); + + /// Handles data from passive packages + void ReadPassiveMode(std::span<u8> buffer); + + /// Handles data from active packages + void ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status); + + /// Handles data from nfc or ir packages + void ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status); + + void UpdateColor(const Color& color); + +private: + void UpdateActiveLeftPadInput(const InputReportActive& input, + const MotionStatus& motion_status); + void UpdateActiveRightPadInput(const InputReportActive& input, + const MotionStatus& motion_status); + void UpdateActiveProPadInput(const InputReportActive& input, const MotionStatus& motion_status); + + void UpdatePasiveLeftPadInput(const InputReportPassive& buffer); + void UpdatePasiveRightPadInput(const InputReportPassive& buffer); + void UpdatePasiveProPadInput(const InputReportPassive& buffer); + + /// Returns a calibrated joystick axis from raw axis data + f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const; + + /// Returns a calibrated accelerometer axis from raw motion data + f32 GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal, + AccelerometerSensitivity sensitivity) const; + + /// Returns a calibrated gyro axis from raw motion data + f32 GetGyroValue(s16 raw_value, const MotionSensorCalibration& cal, + GyroSensitivity sensitivity) const; + + /// Returns a raw motion value from a buffer + s16 GetRawIMUValues(size_t sensor, size_t axis, const InputReportActive& input) const; + + /// Returns motion data from a buffer + MotionData GetMotionInput(const InputReportActive& input, + const MotionStatus& motion_status) const; + + ControllerType device_type{}; + + // Device calibration + JoyStickCalibration left_stick_calibration{}; + JoyStickCalibration right_stick_calibration{}; + MotionCalibration motion_calibration{}; + + Joycon::JoyconCallbacks callbacks{}; +}; + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp new file mode 100644 index 0000000000..17ee388634 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/rumble.cpp @@ -0,0 +1,299 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "input_common/helpers/joycon_protocol/rumble.h" + +namespace InputCommon::Joycon { + +RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle) + : JoyconCommonProtocol(handle) {} + +DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { + LOG_DEBUG(Input, "Enable Rumble"); + const std::vector<u8> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; + std::vector<u8> output; + SetBlocking(); + const auto result = SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer, output); + SetNonBlocking(); + return result; +} + +DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) { + std::vector<u8> buffer(sizeof(DefaultVibrationBuffer)); + + if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) { + return SendVibrationReport(DefaultVibrationBuffer); + } + + // Protect joycons from damage from strong vibrations + const f32 clamp_amplitude = + 1.0f / std::max(1.0f, vibration.high_amplitude + vibration.low_amplitude); + + const u16 encoded_high_frequency = EncodeHighFrequency(vibration.high_frequency); + const u8 encoded_high_amplitude = + EncodeHighAmplitude(vibration.high_amplitude * clamp_amplitude); + const u8 encoded_low_frequency = EncodeLowFrequency(vibration.low_frequency); + const u16 encoded_low_amplitude = EncodeLowAmplitude(vibration.low_amplitude * clamp_amplitude); + + buffer[0] = static_cast<u8>(encoded_high_frequency & 0xFF); + buffer[1] = static_cast<u8>(encoded_high_amplitude | ((encoded_high_frequency >> 8) & 0x01)); + buffer[2] = static_cast<u8>(encoded_low_frequency | ((encoded_low_amplitude >> 8) & 0x80)); + buffer[3] = static_cast<u8>(encoded_low_amplitude & 0xFF); + + // Duplicate rumble for now + buffer[4] = buffer[0]; + buffer[5] = buffer[1]; + buffer[6] = buffer[2]; + buffer[7] = buffer[3]; + + return SendVibrationReport(buffer); +} + +u16 RumbleProtocol::EncodeHighFrequency(f32 frequency) const { + const u8 new_frequency = + static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f)); + return static_cast<u16>((new_frequency - 0x60) * 4); +} + +u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const { + const u8 new_frequency = + static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f)); + return static_cast<u8>(new_frequency - 0x40); +} + +u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const { + /* More information about these values can be found here: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md + */ + constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ + std::pair<f32, int>{0.0f, 0x0}, + {0.01f, 0x2}, + {0.012f, 0x4}, + {0.014f, 0x6}, + {0.017f, 0x8}, + {0.02f, 0x0a}, + {0.024f, 0x0c}, + {0.028f, 0x0e}, + {0.033f, 0x10}, + {0.04f, 0x12}, + {0.047f, 0x14}, + {0.056f, 0x16}, + {0.067f, 0x18}, + {0.08f, 0x1a}, + {0.095f, 0x1c}, + {0.112f, 0x1e}, + {0.117f, 0x20}, + {0.123f, 0x22}, + {0.128f, 0x24}, + {0.134f, 0x26}, + {0.14f, 0x28}, + {0.146f, 0x2a}, + {0.152f, 0x2c}, + {0.159f, 0x2e}, + {0.166f, 0x30}, + {0.173f, 0x32}, + {0.181f, 0x34}, + {0.189f, 0x36}, + {0.198f, 0x38}, + {0.206f, 0x3a}, + {0.215f, 0x3c}, + {0.225f, 0x3e}, + {0.23f, 0x40}, + {0.235f, 0x42}, + {0.24f, 0x44}, + {0.245f, 0x46}, + {0.251f, 0x48}, + {0.256f, 0x4a}, + {0.262f, 0x4c}, + {0.268f, 0x4e}, + {0.273f, 0x50}, + {0.279f, 0x52}, + {0.286f, 0x54}, + {0.292f, 0x56}, + {0.298f, 0x58}, + {0.305f, 0x5a}, + {0.311f, 0x5c}, + {0.318f, 0x5e}, + {0.325f, 0x60}, + {0.332f, 0x62}, + {0.34f, 0x64}, + {0.347f, 0x66}, + {0.355f, 0x68}, + {0.362f, 0x6a}, + {0.37f, 0x6c}, + {0.378f, 0x6e}, + {0.387f, 0x70}, + {0.395f, 0x72}, + {0.404f, 0x74}, + {0.413f, 0x76}, + {0.422f, 0x78}, + {0.431f, 0x7a}, + {0.44f, 0x7c}, + {0.45f, 0x7e}, + {0.46f, 0x80}, + {0.47f, 0x82}, + {0.48f, 0x84}, + {0.491f, 0x86}, + {0.501f, 0x88}, + {0.512f, 0x8a}, + {0.524f, 0x8c}, + {0.535f, 0x8e}, + {0.547f, 0x90}, + {0.559f, 0x92}, + {0.571f, 0x94}, + {0.584f, 0x96}, + {0.596f, 0x98}, + {0.609f, 0x9a}, + {0.623f, 0x9c}, + {0.636f, 0x9e}, + {0.65f, 0xa0}, + {0.665f, 0xa2}, + {0.679f, 0xa4}, + {0.694f, 0xa6}, + {0.709f, 0xa8}, + {0.725f, 0xaa}, + {0.741f, 0xac}, + {0.757f, 0xae}, + {0.773f, 0xb0}, + {0.79f, 0xb2}, + {0.808f, 0xb4}, + {0.825f, 0xb6}, + {0.843f, 0xb8}, + {0.862f, 0xba}, + {0.881f, 0xbc}, + {0.9f, 0xbe}, + {0.92f, 0xc0}, + {0.94f, 0xc2}, + {0.96f, 0xc4}, + {0.981f, 0xc6}, + {1.003f, 0xc8}, + }; + + for (const auto& [amplitude_value, code] : high_fequency_amplitude) { + if (amplitude <= amplitude_value) { + return static_cast<u8>(code); + } + } + + return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); +} + +u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const { + /* More information about these values can be found here: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md + */ + constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ + std::pair<f32, int>{0.0f, 0x0040}, + {0.01f, 0x8040}, + {0.012f, 0x0041}, + {0.014f, 0x8041}, + {0.017f, 0x0042}, + {0.02f, 0x8042}, + {0.024f, 0x0043}, + {0.028f, 0x8043}, + {0.033f, 0x0044}, + {0.04f, 0x8044}, + {0.047f, 0x0045}, + {0.056f, 0x8045}, + {0.067f, 0x0046}, + {0.08f, 0x8046}, + {0.095f, 0x0047}, + {0.112f, 0x8047}, + {0.117f, 0x0048}, + {0.123f, 0x8048}, + {0.128f, 0x0049}, + {0.134f, 0x8049}, + {0.14f, 0x004a}, + {0.146f, 0x804a}, + {0.152f, 0x004b}, + {0.159f, 0x804b}, + {0.166f, 0x004c}, + {0.173f, 0x804c}, + {0.181f, 0x004d}, + {0.189f, 0x804d}, + {0.198f, 0x004e}, + {0.206f, 0x804e}, + {0.215f, 0x004f}, + {0.225f, 0x804f}, + {0.23f, 0x0050}, + {0.235f, 0x8050}, + {0.24f, 0x0051}, + {0.245f, 0x8051}, + {0.251f, 0x0052}, + {0.256f, 0x8052}, + {0.262f, 0x0053}, + {0.268f, 0x8053}, + {0.273f, 0x0054}, + {0.279f, 0x8054}, + {0.286f, 0x0055}, + {0.292f, 0x8055}, + {0.298f, 0x0056}, + {0.305f, 0x8056}, + {0.311f, 0x0057}, + {0.318f, 0x8057}, + {0.325f, 0x0058}, + {0.332f, 0x8058}, + {0.34f, 0x0059}, + {0.347f, 0x8059}, + {0.355f, 0x005a}, + {0.362f, 0x805a}, + {0.37f, 0x005b}, + {0.378f, 0x805b}, + {0.387f, 0x005c}, + {0.395f, 0x805c}, + {0.404f, 0x005d}, + {0.413f, 0x805d}, + {0.422f, 0x005e}, + {0.431f, 0x805e}, + {0.44f, 0x005f}, + {0.45f, 0x805f}, + {0.46f, 0x0060}, + {0.47f, 0x8060}, + {0.48f, 0x0061}, + {0.491f, 0x8061}, + {0.501f, 0x0062}, + {0.512f, 0x8062}, + {0.524f, 0x0063}, + {0.535f, 0x8063}, + {0.547f, 0x0064}, + {0.559f, 0x8064}, + {0.571f, 0x0065}, + {0.584f, 0x8065}, + {0.596f, 0x0066}, + {0.609f, 0x8066}, + {0.623f, 0x0067}, + {0.636f, 0x8067}, + {0.65f, 0x0068}, + {0.665f, 0x8068}, + {0.679f, 0x0069}, + {0.694f, 0x8069}, + {0.709f, 0x006a}, + {0.725f, 0x806a}, + {0.741f, 0x006b}, + {0.757f, 0x806b}, + {0.773f, 0x006c}, + {0.79f, 0x806c}, + {0.808f, 0x006d}, + {0.825f, 0x806d}, + {0.843f, 0x006e}, + {0.862f, 0x806e}, + {0.881f, 0x006f}, + {0.9f, 0x806f}, + {0.92f, 0x0070}, + {0.94f, 0x8070}, + {0.96f, 0x0071}, + {0.981f, 0x8071}, + {1.003f, 0x0072}, + }; + + for (const auto& [amplitude_value, code] : high_fequency_amplitude) { + if (amplitude <= amplitude_value) { + return static_cast<u16>(code); + } + } + + return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); +} + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h new file mode 100644 index 0000000000..7d0329f039 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/rumble.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse +// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c +// https://github.com/CTCaer/jc_toolkit +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + +#pragma once + +#include <vector> + +#include "input_common/helpers/joycon_protocol/common_protocol.h" +#include "input_common/helpers/joycon_protocol/joycon_types.h" + +namespace InputCommon::Joycon { + +class RumbleProtocol final : private JoyconCommonProtocol { +public: + RumbleProtocol(std::shared_ptr<JoyconHandle> handle); + + DriverResult EnableRumble(bool is_enabled); + + DriverResult SendVibration(const VibrationValue& vibration); + +private: + u16 EncodeHighFrequency(f32 frequency) const; + u8 EncodeLowFrequency(f32 frequency) const; + u8 EncodeHighAmplitude(f32 amplitude) const; + u16 EncodeLowAmplitude(f32 amplitude) const; +}; + +} // namespace InputCommon::Joycon -- cgit v1.2.3-70-g09d2 From 6e33731f29bb870ad28416aff8db32854f4a4fa5 Mon Sep 17 00:00:00 2001 From: Narr the Reg <juangerman-13@hotmail.com> Date: Wed, 21 Dec 2022 10:44:23 -0600 Subject: input_common: Add dual joycon support --- src/input_common/drivers/joycon.cpp | 125 +++++++++++++++++++++++++++++------- 1 file changed, 101 insertions(+), 24 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index dbe730e1a3..049ecc4f24 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -55,14 +55,17 @@ void Joycons::Reset() { void Joycons::Setup() { u32 port = 0; + PreSetController(GetIdentifier(0, Joycon::ControllerType::None)); for (auto& device : left_joycons) { PreSetController(GetIdentifier(port, Joycon::ControllerType::Left)); device = std::make_shared<Joycon::JoyconDriver>(port++); } + port = 0; for (auto& device : right_joycons) { PreSetController(GetIdentifier(port, Joycon::ControllerType::Right)); device = std::make_shared<Joycon::JoyconDriver>(port++); } + port = 0; for (auto& device : pro_joycons) { PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro)); device = std::make_shared<Joycon::JoyconDriver>(port++); @@ -109,7 +112,7 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { return false; } - auto is_handle_identical = [&](std::shared_ptr<Joycon::JoyconDriver> device) { + auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) { if (!device) { return false; } @@ -445,7 +448,7 @@ std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { return; } std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()), - device->GetDevicePort()); + device->GetDevicePort() + 1); devices.emplace_back(Common::ParamPackage{ {"engine", GetEngineName()}, {"display", std::move(name)}, @@ -464,32 +467,49 @@ std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { add_entry(controller); } + // List dual joycon pairs + for (std::size_t i = 0; i < MaxSupportedControllers; i++) { + if (!left_joycons[i] || !right_joycons[i]) { + continue; + } + if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) { + continue; + } + constexpr auto type = Joycon::ControllerType::Dual; + std::string name = fmt::format("{} {}", JoyconName(type), i + 1); + devices.emplace_back(Common::ParamPackage{ + {"engine", GetEngineName()}, + {"display", std::move(name)}, + {"port", std::to_string(i)}, + {"pad", std::to_string(static_cast<std::size_t>(type))}, + }); + } + return devices; } ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) { - static constexpr std::array<std::pair<Settings::NativeButton::Values, Joycon::PadButton>, 20> + static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>, + 18> switch_to_joycon_button = { - std::pair{Settings::NativeButton::A, Joycon::PadButton::A}, - {Settings::NativeButton::B, Joycon::PadButton::B}, - {Settings::NativeButton::X, Joycon::PadButton::X}, - {Settings::NativeButton::Y, Joycon::PadButton::Y}, - {Settings::NativeButton::DLeft, Joycon::PadButton::Left}, - {Settings::NativeButton::DUp, Joycon::PadButton::Up}, - {Settings::NativeButton::DRight, Joycon::PadButton::Right}, - {Settings::NativeButton::DDown, Joycon::PadButton::Down}, - {Settings::NativeButton::SL, Joycon::PadButton::LeftSL}, - {Settings::NativeButton::SR, Joycon::PadButton::LeftSR}, - {Settings::NativeButton::L, Joycon::PadButton::L}, - {Settings::NativeButton::R, Joycon::PadButton::R}, - {Settings::NativeButton::ZL, Joycon::PadButton::ZL}, - {Settings::NativeButton::ZR, Joycon::PadButton::ZR}, - {Settings::NativeButton::Plus, Joycon::PadButton::Plus}, - {Settings::NativeButton::Minus, Joycon::PadButton::Minus}, - {Settings::NativeButton::Home, Joycon::PadButton::Home}, - {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture}, - {Settings::NativeButton::LStick, Joycon::PadButton::StickL}, - {Settings::NativeButton::RStick, Joycon::PadButton::StickR}, + std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true}, + {Settings::NativeButton::B, Joycon::PadButton::B, true}, + {Settings::NativeButton::X, Joycon::PadButton::X, true}, + {Settings::NativeButton::Y, Joycon::PadButton::Y, true}, + {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false}, + {Settings::NativeButton::DUp, Joycon::PadButton::Up, false}, + {Settings::NativeButton::DRight, Joycon::PadButton::Right, false}, + {Settings::NativeButton::DDown, Joycon::PadButton::Down, false}, + {Settings::NativeButton::L, Joycon::PadButton::L, false}, + {Settings::NativeButton::R, Joycon::PadButton::R, true}, + {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false}, + {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true}, + {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true}, + {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false}, + {Settings::NativeButton::Home, Joycon::PadButton::Home, true}, + {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false}, + {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false}, + {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true}, }; if (!params.Has("port")) { @@ -497,14 +517,51 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par } ButtonMapping mapping{}; - for (const auto& [switch_button, joycon_button] : switch_to_joycon_button) { + for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) { + int pad = params.Get("pad", 0); + if (pad == static_cast<int>(Joycon::ControllerType::Dual)) { + pad = side ? static_cast<int>(Joycon::ControllerType::Right) + : static_cast<int>(Joycon::ControllerType::Left); + } + Common::ParamPackage button_params{}; button_params.Set("engine", GetEngineName()); button_params.Set("port", params.Get("port", 0)); + button_params.Set("pad", pad); button_params.Set("button", static_cast<int>(joycon_button)); mapping.insert_or_assign(switch_button, std::move(button_params)); } + // Map SL and SR buttons for left joycons + if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) { + Common::ParamPackage button_params{}; + button_params.Set("engine", GetEngineName()); + button_params.Set("port", params.Get("port", 0)); + button_params.Set("pad", static_cast<int>(Joycon::ControllerType::Left)); + + Common::ParamPackage sl_button_params = button_params; + Common::ParamPackage sr_button_params = button_params; + sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL)); + sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR)); + mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); + } + + // Map SL and SR buttons for right joycons + if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) { + Common::ParamPackage button_params{}; + button_params.Set("engine", GetEngineName()); + button_params.Set("port", params.Get("port", 0)); + button_params.Set("pad", static_cast<int>(Joycon::ControllerType::Right)); + + Common::ParamPackage sl_button_params = button_params; + Common::ParamPackage sr_button_params = button_params; + sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL)); + sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR)); + mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); + } + return mapping; } @@ -513,16 +570,25 @@ AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& par return {}; } + int pad_left = params.Get("pad", 0); + int pad_right = pad_left; + if (pad_left == static_cast<int>(Joycon::ControllerType::Dual)) { + pad_left = static_cast<int>(Joycon::ControllerType::Left); + pad_right = static_cast<int>(Joycon::ControllerType::Right); + } + AnalogMapping mapping = {}; Common::ParamPackage left_analog_params; left_analog_params.Set("engine", GetEngineName()); left_analog_params.Set("port", params.Get("port", 0)); + left_analog_params.Set("pad", pad_left); left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX)); left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY)); mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); Common::ParamPackage right_analog_params; right_analog_params.Set("engine", GetEngineName()); right_analog_params.Set("port", params.Get("port", 0)); + right_analog_params.Set("pad", pad_right); right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX)); right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY)); mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params)); @@ -534,15 +600,24 @@ MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& par return {}; } + int pad_left = params.Get("pad", 0); + int pad_right = pad_left; + if (pad_left == static_cast<int>(Joycon::ControllerType::Dual)) { + pad_left = static_cast<int>(Joycon::ControllerType::Left); + pad_right = static_cast<int>(Joycon::ControllerType::Right); + } + MotionMapping mapping = {}; Common::ParamPackage left_motion_params; left_motion_params.Set("engine", GetEngineName()); left_motion_params.Set("port", params.Get("port", 0)); + left_motion_params.Set("pad", pad_left); left_motion_params.Set("motion", 0); mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params)); Common::ParamPackage right_Motion_params; right_Motion_params.Set("engine", GetEngineName()); right_Motion_params.Set("port", params.Get("port", 0)); + right_Motion_params.Set("pad", pad_right); right_Motion_params.Set("motion", 1); mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params)); return mapping; @@ -622,6 +697,8 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const { return "Pro Controller"; case Joycon::ControllerType::Grip: return "Grip Controller"; + case Joycon::ControllerType::Dual: + return "Dual Joycon"; default: return "Unknow Joycon"; } -- cgit v1.2.3-70-g09d2 From 6d6b7bdbc327528d155f0422ef096846559844c0 Mon Sep 17 00:00:00 2001 From: german77 <juangerman-13@hotmail.com> Date: Thu, 22 Dec 2022 01:07:46 -0600 Subject: input_common: Implement joycon nfc --- src/core/hid/emulated_controller.cpp | 3 +- src/input_common/CMakeLists.txt | 2 + src/input_common/drivers/joycon.cpp | 4 +- src/input_common/helpers/joycon_driver.cpp | 44 +++ src/input_common/helpers/joycon_driver.h | 24 +- src/input_common/helpers/joycon_protocol/nfc.cpp | 414 +++++++++++++++++++++ src/input_common/helpers/joycon_protocol/nfc.h | 61 +++ .../helpers/joycon_protocol/poller.cpp | 4 + src/input_common/helpers/joycon_protocol/poller.h | 1 + 9 files changed, 544 insertions(+), 13 deletions(-) create mode 100644 src/input_common/helpers/joycon_protocol/nfc.cpp create mode 100644 src/input_common/helpers/joycon_protocol/nfc.h (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 89638cb856..1e4ec4addc 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -144,7 +144,8 @@ void EmulatedController::LoadDevices() { battery_params[RightIndex].Set("battery", true); camera_params = Common::ParamPackage{"engine:camera,camera:1"}; - nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; + nfc_params = right_joycon; + nfc_params.Set("nfc", true); ring_params = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; output_params[LeftIndex] = left_joycon; diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index addecc9b3b..9c901af2ab 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -64,6 +64,8 @@ if (ENABLE_SDL2) helpers/joycon_protocol/generic_functions.cpp helpers/joycon_protocol/generic_functions.h helpers/joycon_protocol/joycon_types.h + helpers/joycon_protocol/nfc.cpp + helpers/joycon_protocol/nfc.h helpers/joycon_protocol/poller.cpp helpers/joycon_protocol/poller.h helpers/joycon_protocol/ringcon.cpp diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 049ecc4f24..29f0dc0c89 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -388,7 +388,9 @@ void Joycons::OnRingConUpdate(f32 ring_data) { void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) { const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right); - SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); + const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved + : Common::Input::NfcState::NewAmiibo; + SetNfc(identifier, {nfc_state, amiibo_data}); } std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const { diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index c0a03fe2e1..c3debffd11 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -5,6 +5,12 @@ #include "common/swap.h" #include "common/thread.h" #include "input_common/helpers/joycon_driver.h" +#include "input_common/helpers/joycon_protocol/calibration.h" +#include "input_common/helpers/joycon_protocol/generic_functions.h" +#include "input_common/helpers/joycon_protocol/nfc.h" +#include "input_common/helpers/joycon_protocol/poller.h" +#include "input_common/helpers/joycon_protocol/ringcon.h" +#include "input_common/helpers/joycon_protocol/rumble.h" namespace InputCommon::Joycon { JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} { @@ -72,6 +78,7 @@ DriverResult JoyconDriver::InitializeDevice() { // Initialize HW Protocols calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle); generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); + nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle); ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle); rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); @@ -193,6 +200,25 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { .min_value = ring_calibration.min_value, }; + if (nfc_protocol->IsEnabled()) { + if (amiibo_detected) { + if (!nfc_protocol->HasAmiibo()) { + joycon_poller->updateAmiibo({}); + amiibo_detected = false; + return; + } + } + + if (!amiibo_detected) { + std::vector<u8> data(0x21C); + const auto result = nfc_protocol->ScanAmiibo(data); + if (result == DriverResult::Success) { + joycon_poller->updateAmiibo(data); + amiibo_detected = true; + } + } + } + switch (report_mode) { case InputReport::STANDARD_FULL_60HZ: joycon_poller->ReadActiveMode(buffer, motion_status, ring_status); @@ -225,6 +251,24 @@ void JoyconDriver::SetPollingMode() { generic_protocol->EnableImu(false); } + if (nfc_protocol->IsEnabled()) { + amiibo_detected = false; + nfc_protocol->DisableNfc(); + } + + 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; + } + nfc_protocol->DisableNfc(); + LOG_ERROR(Input, "Error enabling NFC"); + } + if (ring_protocol->IsEnabled()) { ring_connected = false; ring_protocol->DisableRingCon(); diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index dc5d60221a..c9118ee939 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -8,14 +8,15 @@ #include <span> #include <thread> -#include "input_common/helpers/joycon_protocol/calibration.h" -#include "input_common/helpers/joycon_protocol/generic_functions.h" #include "input_common/helpers/joycon_protocol/joycon_types.h" -#include "input_common/helpers/joycon_protocol/poller.h" -#include "input_common/helpers/joycon_protocol/ringcon.h" -#include "input_common/helpers/joycon_protocol/rumble.h" namespace InputCommon::Joycon { +class CalibrationProtocol; +class GenericProtocol; +class NfcProtocol; +class JoyconPoller; +class RingConProtocol; +class RumbleProtocol; class JoyconDriver final { public: @@ -84,17 +85,18 @@ private: SupportedFeatures GetSupportedFeatures(); // Protocol Features - std::unique_ptr<CalibrationProtocol> calibration_protocol = nullptr; - std::unique_ptr<GenericProtocol> generic_protocol = nullptr; - std::unique_ptr<JoyconPoller> joycon_poller = nullptr; - std::unique_ptr<RingConProtocol> ring_protocol = nullptr; - std::unique_ptr<RumbleProtocol> rumble_protocol = nullptr; + std::unique_ptr<CalibrationProtocol> calibration_protocol; + std::unique_ptr<GenericProtocol> generic_protocol; + std::unique_ptr<NfcProtocol> nfc_protocol; + std::unique_ptr<JoyconPoller> joycon_poller; + std::unique_ptr<RingConProtocol> ring_protocol; + std::unique_ptr<RumbleProtocol> rumble_protocol; // Connection status bool is_connected{}; u64 delta_time; std::size_t error_counter{}; - std::shared_ptr<JoyconHandle> hidapi_handle = nullptr; + std::shared_ptr<JoyconHandle> hidapi_handle; std::chrono::time_point<std::chrono::steady_clock> last_update; // External device status 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 0000000000..69b2bfe050 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -0,0 +1,414 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <thread> +#include "common/logging/log.h" +#include "input_common/helpers/joycon_protocol/nfc.h" + +namespace InputCommon::Joycon { + +NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle) : JoyconCommonProtocol(handle) {} + +DriverResult NfcProtocol::EnableNfc() { + LOG_INFO(Input, "Enable NFC"); + DriverResult result{DriverResult::Success}; + SetBlocking(); + + if (result == DriverResult::Success) { + result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); + } + if (result == DriverResult::Success) { + result = EnableMCU(true); + } + if (result == DriverResult::Success) { + result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); + } + if (result == DriverResult::Success) { + const MCUConfig config{ + .command = MCUCommand::ConfigureMCU, + .sub_command = MCUSubCommand::SetMCUMode, + .mode = MCUMode::NFC, + .crc = {}, + }; + + result = ConfigureMCU(config); + } + + SetNonBlocking(); + return result; +} + +DriverResult NfcProtocol::DisableNfc() { + LOG_DEBUG(Input, "Disable NFC"); + DriverResult result{DriverResult::Success}; + SetBlocking(); + + if (result == DriverResult::Success) { + result = EnableMCU(false); + } + + is_enabled = false; + + SetNonBlocking(); + return result; +} + +DriverResult NfcProtocol::StartNFCPollingMode() { + LOG_DEBUG(Input, "Start NFC pooling Mode"); + DriverResult result{DriverResult::Success}; + TagFoundData tag_data{}; + SetBlocking(); + + if (result == DriverResult::Success) { + result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); + } + if (result == DriverResult::Success) { + result = WaitUntilNfcIsReady(); + } + if (result == DriverResult::Success) { + is_enabled = true; + } + + SetNonBlocking(); + return result; +} + +DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { + LOG_DEBUG(Input, "Start NFC pooling Mode"); + DriverResult result{DriverResult::Success}; + TagFoundData tag_data{}; + SetBlocking(); + + if (result == DriverResult::Success) { + result = StartPolling(tag_data); + } + if (result == DriverResult::Success) { + result = ReadTag(tag_data); + } + if (result == DriverResult::Success) { + result = WaitUntilNfcIsReady(); + } + if (result == DriverResult::Success) { + result = StartPolling(tag_data); + } + if (result == DriverResult::Success) { + result = GetAmiiboData(data); + } + + SetNonBlocking(); + return result; +} + +bool NfcProtocol::HasAmiibo() { + DriverResult result{DriverResult::Success}; + TagFoundData tag_data{}; + SetBlocking(); + + if (result == DriverResult::Success) { + result = StartPolling(tag_data); + } + + SetNonBlocking(); + return result == DriverResult::Success; +} + +DriverResult NfcProtocol::WaitUntilNfcIsReady() { + constexpr std::size_t timeout_limit = 10; + std::vector<u8> output; + std::size_t tries = 0; + + do { + auto result = SendStartWaitingRecieveRequest(output); + + if (result != DriverResult::Success) { + return result; + } + if (tries++ > timeout_limit) { + return DriverResult::Timeout; + } + } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 || + output[56] != 0x00); + + return DriverResult::Success; +} + +DriverResult NfcProtocol::StartPolling(TagFoundData& data) { + LOG_DEBUG(Input, "Start Polling for tag"); + constexpr std::size_t timeout_limit = 7; + std::vector<u8> output; + std::size_t tries = 0; + + do { + const auto result = SendStartPollingRequest(output); + if (result != DriverResult::Success) { + return result; + } + if (tries++ > timeout_limit) { + return DriverResult::Timeout; + } + } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[56] != 0x09); + + data.type = output[62]; + data.uuid.resize(output[64]); + memcpy(data.uuid.data(), output.data() + 65, data.uuid.size()); + + return DriverResult::Success; +} + +DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { + constexpr std::size_t timeout_limit = 10; + std::vector<u8> output; + std::size_t tries = 0; + + std::string uuid_string = ""; + for (auto& content : data.uuid) { + uuid_string += " " + fmt::format("{:02x}", content); + } + + LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string); + + tries = 0; + std::size_t ntag_pages = 0; + // Read Tag data +loop1: + while (true) { + auto result = SendReadAmiiboRequest(output, ntag_pages); + + int attempt = 0; + while (1) { + if (attempt != 0) { + result = GetMCUDataResponse(ReportMode::NFC_IR_MODE_60HZ, output); + } + if ((output[49] == 0x3a || output[49] == 0x2a) && output[56] == 0x07) { + return DriverResult::ErrorReadingData; + } + if (output[49] == 0x3a && output[51] == 0x07 && output[52] == 0x01) { + if (data.type != 2) { + goto loop1; + } + switch (output[74]) { + case 0: + ntag_pages = 135; + break; + case 3: + ntag_pages = 45; + break; + case 4: + ntag_pages = 231; + break; + default: + return DriverResult::ErrorReadingData; + } + goto loop1; + } + if (output[49] == 0x2a && output[56] == 0x04) { + // finished + SendStopPollingRequest(output); + return DriverResult::Success; + } + if (output[49] == 0x2a) { + goto loop1; + } + if (attempt++ > 6) { + goto loop1; + } + } + + if (result != DriverResult::Success) { + return result; + } + if (tries++ > timeout_limit) { + return DriverResult::Timeout; + } + } + + return DriverResult::Success; +} + +DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { + constexpr std::size_t timeout_limit = 10; + std::vector<u8> output; + std::size_t tries = 0; + + std::size_t ntag_pages = 135; + std::size_t ntag_buffer_pos = 0; + // Read Tag data +loop1: + while (true) { + auto result = SendReadAmiiboRequest(output, ntag_pages); + + int attempt = 0; + while (1) { + if (attempt != 0) { + result = GetMCUDataResponse(ReportMode::NFC_IR_MODE_60HZ, output); + } + if ((output[49] == 0x3a || output[49] == 0x2a) && output[56] == 0x07) { + return DriverResult::ErrorReadingData; + } + if (output[49] == 0x3a && output[51] == 0x07) { + std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; + if (output[52] == 0x01) { + memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, + payload_size - 60); + ntag_buffer_pos += payload_size - 60; + } else { + memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size); + } + goto loop1; + } + if (output[49] == 0x2a && output[56] == 0x04) { + LOG_INFO(Input, "Finished reading amiibo"); + return DriverResult::Success; + } + if (output[49] == 0x2a) { + goto loop1; + } + if (attempt++ > 4) { + goto loop1; + } + } + + if (result != DriverResult::Success) { + return result; + } + if (tries++ > timeout_limit) { + return DriverResult::Timeout; + } + } + + return DriverResult::Success; +} + +DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) { + NFCRequestState request{ + .sub_command = MCUSubCommand::ReadDeviceMode, + .command_argument = NFCReadCommand::StartPolling, + .packet_id = 0x0, + .packet_flag = MCUPacketFlag::LastCommandPacket, + .data_length = sizeof(NFCPollingCommandData), + .nfc_polling = + { + .enable_mifare = 0x01, + .unknown_1 = 0x00, + .unknown_2 = 0x00, + .unknown_3 = 0x2c, + .unknown_4 = 0x01, + }, + .crc = {}, + }; + + std::vector<u8> request_data(sizeof(NFCRequestState)); + memcpy(request_data.data(), &request, sizeof(NFCRequestState)); + request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); +} + +DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) { + NFCRequestState request{ + .sub_command = MCUSubCommand::ReadDeviceMode, + .command_argument = NFCReadCommand::StopPolling, + .packet_id = 0x0, + .packet_flag = MCUPacketFlag::LastCommandPacket, + .data_length = 0, + .raw_data = {}, + .crc = {}, + }; + + std::vector<u8> request_data(sizeof(NFCRequestState)); + memcpy(request_data.data(), &request, sizeof(NFCRequestState)); + request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); +} + +DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output) { + NFCRequestState request{ + .sub_command = MCUSubCommand::ReadDeviceMode, + .command_argument = NFCReadCommand::StartWaitingRecieve, + .packet_id = 0x0, + .packet_flag = MCUPacketFlag::LastCommandPacket, + .data_length = 0, + .raw_data = {}, + .crc = {}, + }; + + std::vector<u8> request_data(sizeof(NFCRequestState)); + memcpy(request_data.data(), &request, sizeof(NFCRequestState)); + request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); +} + +DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, std::size_t ntag_pages) { + NFCRequestState request{ + .sub_command = MCUSubCommand::ReadDeviceMode, + .command_argument = NFCReadCommand::Ntag, + .packet_id = 0x0, + .packet_flag = MCUPacketFlag::LastCommandPacket, + .data_length = sizeof(NFCReadCommandData), + .nfc_read = + { + .unknown = 0xd0, + .uuid_length = 0x07, + .unknown_2 = 0x00, + .uid = {}, + .tag_type = NFCTagType::AllTags, + .read_block = GetReadBlockCommand(ntag_pages), + }, + .crc = {}, + }; + + std::vector<u8> request_data(sizeof(NFCRequestState)); + memcpy(request_data.data(), &request, sizeof(NFCRequestState)); + request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); +} + +NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(std::size_t pages) const { + if (pages == 0) { + return { + .block_count = 1, + }; + } + + if (pages == 45) { + return { + .block_count = 1, + .blocks = + { + NFCReadBlock{0x00, 0x2C}, + }, + }; + } + + if (pages == 135) { + return { + .block_count = 3, + .blocks = + { + NFCReadBlock{0x00, 0x3b}, + {0x3c, 0x77}, + {0x78, 0x86}, + }, + }; + } + + if (pages == 231) { + return { + .block_count = 4, + .blocks = + { + NFCReadBlock{0x00, 0x3b}, + {0x3c, 0x77}, + {0x78, 0x83}, + {0xb4, 0xe6}, + }, + }; + } + + return {}; +} + +bool NfcProtocol::IsEnabled() { + return is_enabled; +} + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h new file mode 100644 index 0000000000..0ede03d505 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse +// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c +// https://github.com/CTCaer/jc_toolkit +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + +#pragma once + +#include <vector> + +#include "input_common/helpers/joycon_protocol/common_protocol.h" +#include "input_common/helpers/joycon_protocol/joycon_types.h" + +namespace InputCommon::Joycon { + +class NfcProtocol final : private JoyconCommonProtocol { +public: + NfcProtocol(std::shared_ptr<JoyconHandle> handle); + + DriverResult EnableNfc(); + + DriverResult DisableNfc(); + + DriverResult StartNFCPollingMode(); + + DriverResult ScanAmiibo(std::vector<u8>& data); + + bool HasAmiibo(); + + bool IsEnabled(); + +private: + struct TagFoundData { + u8 type; + std::vector<u8> uuid; + }; + + DriverResult WaitUntilNfcIsReady(); + + DriverResult StartPolling(TagFoundData& data); + + DriverResult ReadTag(const TagFoundData& data); + + DriverResult GetAmiiboData(std::vector<u8>& data); + + DriverResult SendStartPollingRequest(std::vector<u8>& output); + + DriverResult SendStopPollingRequest(std::vector<u8>& output); + + DriverResult SendStartWaitingRecieveRequest(std::vector<u8>& output); + + DriverResult SendReadAmiiboRequest(std::vector<u8>& output, std::size_t ntag_pages); + + NFCReadBlockCommand GetReadBlockCommand(std::size_t pages) const; + + bool is_enabled{}; +}; + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index cb76e1e06f..fd05d98f38 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -74,6 +74,10 @@ 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::UpdateRing(s16 value, const RingStatus& ring_status) { float normalized_value = static_cast<float>(value - ring_status.default_value); if (normalized_value > 0) { diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h index 68b14b8ba0..c40fc7bca8 100644 --- a/src/input_common/helpers/joycon_protocol/poller.h +++ b/src/input_common/helpers/joycon_protocol/poller.h @@ -36,6 +36,7 @@ public: void UpdateColor(const Color& color); void UpdateRing(s16 value, const RingStatus& ring_status); + void updateAmiibo(const std::vector<u8>& amiibo_data); private: void UpdateActiveLeftPadInput(const InputReportActive& input, -- cgit v1.2.3-70-g09d2 From e1a3bda4d9881cb99c36b64733b814a3bb437f13 Mon Sep 17 00:00:00 2001 From: german77 <juangerman-13@hotmail.com> Date: Fri, 23 Dec 2022 08:32:02 -0600 Subject: Address review comments --- src/core/hid/input_converter.cpp | 6 ++---- src/input_common/drivers/joycon.cpp | 2 +- src/input_common/helpers/joycon_driver.cpp | 2 +- src/input_common/helpers/joycon_protocol/calibration.cpp | 10 +++------- src/input_common/helpers/joycon_protocol/calibration.h | 2 +- .../helpers/joycon_protocol/common_protocol.cpp | 6 +++--- .../helpers/joycon_protocol/generic_functions.cpp | 12 ++++++------ .../helpers/joycon_protocol/generic_functions.h | 2 +- src/input_common/helpers/joycon_protocol/nfc.cpp | 9 +++++---- src/input_common/helpers/joycon_protocol/nfc.h | 4 ++-- src/input_common/helpers/joycon_protocol/ringcon.cpp | 16 ++++++++-------- src/input_common/helpers/joycon_protocol/ringcon.h | 4 ++-- src/input_common/helpers/joycon_protocol/rumble.cpp | 13 ++++++++----- src/input_common/helpers/joycon_protocol/rumble.h | 2 +- 14 files changed, 44 insertions(+), 46 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index d7e253044a..3f7b8c0904 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -305,17 +305,15 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal } Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) { - Common::Input::BodyColorStatus color{}; switch (callback.type) { case Common::Input::InputType::Color: - color = callback.color_status; + return callback.color_status; break; default: LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type); + return {}; break; } - - return color; } void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 29f0dc0c89..696a6db393 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -316,7 +316,7 @@ void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, return; } - Common::Input::BatteryLevel battery{value.status.Value()}; + Common::Input::BatteryLevel battery{}; switch (value.status) { case 0: battery = Common::Input::BatteryLevel::Empty; diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index db9ff4875e..8982a2397f 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -465,7 +465,7 @@ void JoyconDriver::SetCallbacks(const Joycon::JoyconCallbacks& callbacks) { Joycon::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, ControllerType& controller_type) { - std::array<std::pair<u32, Joycon::ControllerType>, 4> supported_devices{ + static constexpr std::array<std::pair<u32, Joycon::ControllerType>, 4> supported_devices{ std::pair<u32, Joycon::ControllerType>{0x2006, Joycon::ControllerType::Left}, {0x2007, Joycon::ControllerType::Right}, {0x2009, Joycon::ControllerType::Pro}, diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp index ce1ff7061d..cd30ab8698 100644 --- a/src/input_common/helpers/joycon_protocol/calibration.cpp +++ b/src/input_common/helpers/joycon_protocol/calibration.cpp @@ -9,7 +9,7 @@ namespace InputCommon::Joycon { CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle) - : JoyconCommonProtocol(handle) {} + : JoyconCommonProtocol(std::move(handle)) {} DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { std::vector<u8> buffer; @@ -136,12 +136,8 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio ring_data_min = current_value - 800; ring_data_default = current_value; } - if (ring_data_max < current_value) { - ring_data_max = current_value; - } - if (ring_data_min > current_value) { - ring_data_min = current_value; - } + ring_data_max = std::max(ring_data_max, current_value); + ring_data_min = std::min(ring_data_min, current_value); calibration = { .default_value = ring_data_default, .max_value = ring_data_max, diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h index 32ddef4b85..afb52a36a4 100644 --- a/src/input_common/helpers/joycon_protocol/calibration.h +++ b/src/input_common/helpers/joycon_protocol/calibration.h @@ -24,7 +24,7 @@ namespace InputCommon::Joycon { /// Driver functions related to retrieving calibration data from the device class CalibrationProtocol final : private JoyconCommonProtocol { public: - CalibrationProtocol(std::shared_ptr<JoyconHandle> handle); + explicit CalibrationProtocol(std::shared_ptr<JoyconHandle> handle); /** * Sends a request to obtain the left stick calibration from memory diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index 43a036e027..a4d08fdafa 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp @@ -6,7 +6,7 @@ namespace InputCommon::Joycon { JoyconCommonProtocol::JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_) - : hidapi_handle{hidapi_handle_} {} + : hidapi_handle{std::move(hidapi_handle_)} {} u8 JoyconCommonProtocol::GetCounter() { hidapi_handle->packet_counter = (hidapi_handle->packet_counter + 1) & 0x0F; @@ -256,7 +256,7 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod } // crc-8-ccitt / polynomial 0x07 look up table -static constexpr uint8_t mcu_crc8_table[256] = { +constexpr std::array<u8, 256> mcu_crc8_table = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, @@ -278,7 +278,7 @@ u8 JoyconCommonProtocol::CalculateMCU_CRC8(u8* buffer, u8 size) const { u8 crc8 = 0x0; for (int i = 0; i < size; ++i) { - crc8 = mcu_crc8_table[(u8)(crc8 ^ buffer[i])]; + crc8 = mcu_crc8_table[static_cast<u8>(crc8 ^ buffer[i])]; } return crc8; } diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp index 829f7625d6..cbd9ff4f8b 100644 --- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp +++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp @@ -7,7 +7,7 @@ namespace InputCommon::Joycon { GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle) - : JoyconCommonProtocol(handle) {} + : JoyconCommonProtocol(std::move(handle)) {} DriverResult GenericProtocol::EnablePassiveMode() { SetBlocking(); @@ -43,7 +43,7 @@ DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) } DriverResult GenericProtocol::EnableImu(bool enable) { - const std::vector<u8> buffer{static_cast<u8>(enable ? 1 : 0)}; + const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; std::vector<u8> output; SetBlocking(); const auto result = SendSubCommand(SubCommand::ENABLE_IMU, buffer, output); @@ -54,8 +54,8 @@ DriverResult GenericProtocol::EnableImu(bool enable) { DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, AccelerometerSensitivity asen, AccelerometerPerformance afrec) { - const std::vector<u8> buffer{static_cast<u8>(gsen), static_cast<u8>(asen), - static_cast<u8>(gfrec), static_cast<u8>(afrec)}; + const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen), + static_cast<u8>(gfrec), static_cast<u8>(afrec)}; std::vector<u8> output; SetBlocking(); const auto result = SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer, output); @@ -115,7 +115,7 @@ DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) { } DriverResult GenericProtocol::SetHomeLight() { - const std::vector<u8> buffer{0x0f, 0xf0, 0x00}; + static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00}; std::vector<u8> output; SetBlocking(); @@ -130,7 +130,7 @@ DriverResult GenericProtocol::SetLedBusy() { } DriverResult GenericProtocol::SetLedPattern(u8 leds) { - const std::vector<u8> buffer{leds}; + const std::array<u8, 1> buffer{leds}; std::vector<u8> output; SetBlocking(); diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h index c3e2ccadc0..239bb7dbf9 100644 --- a/src/input_common/helpers/joycon_protocol/generic_functions.h +++ b/src/input_common/helpers/joycon_protocol/generic_functions.h @@ -16,7 +16,7 @@ namespace InputCommon::Joycon { /// Joycon driver functions that easily implemented class GenericProtocol final : private JoyconCommonProtocol { public: - GenericProtocol(std::shared_ptr<JoyconHandle> handle); + explicit GenericProtocol(std::shared_ptr<JoyconHandle> handle); /// Enables passive mode. This mode only sends button data on change. Sticks will return digital /// data instead of analog. Motion will be disabled diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index 69b2bfe050..8755e310b8 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -7,7 +7,8 @@ namespace InputCommon::Joycon { -NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle) : JoyconCommonProtocol(handle) {} +NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle) + : JoyconCommonProtocol(std::move(handle)) {} DriverResult NfcProtocol::EnableNfc() { LOG_INFO(Input, "Enable NFC"); @@ -160,9 +161,9 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { std::vector<u8> output; std::size_t tries = 0; - std::string uuid_string = ""; + std::string uuid_string; for (auto& content : data.uuid) { - uuid_string += " " + fmt::format("{:02x}", content); + uuid_string += fmt::format(" {:02x}", content); } LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string); @@ -407,7 +408,7 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(std::size_t pages) const { return {}; } -bool NfcProtocol::IsEnabled() { +bool NfcProtocol::IsEnabled() const { return is_enabled; } diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index 0ede03d505..5cb0e5a652 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -17,7 +17,7 @@ namespace InputCommon::Joycon { class NfcProtocol final : private JoyconCommonProtocol { public: - NfcProtocol(std::shared_ptr<JoyconHandle> handle); + explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle); DriverResult EnableNfc(); @@ -29,7 +29,7 @@ public: bool HasAmiibo(); - bool IsEnabled(); + bool IsEnabled() const; private: struct TagFoundData { diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp index 47769f3441..8adad57dd6 100644 --- a/src/input_common/helpers/joycon_protocol/ringcon.cpp +++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp @@ -7,7 +7,7 @@ namespace InputCommon::Joycon { RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle) - : JoyconCommonProtocol(handle) {} + : JoyconCommonProtocol(std::move(handle)) {} DriverResult RingConProtocol::EnableRingCon() { LOG_DEBUG(Input, "Enable Ringcon"); @@ -78,7 +78,7 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { is_connected = false; do { - std::vector<u8> empty_data(0); + std::array<u8, 1> empty_data{}; const auto result = SendSubCommand(SubCommand::UNKNOWN_RINGCON, empty_data, output); if (result != DriverResult::Success) { @@ -101,11 +101,11 @@ DriverResult RingConProtocol::ConfigureRing() { std::vector<u8> output; std::size_t tries = 0; + static constexpr std::array<u8, 37> ring_config{ + 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36, + 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; do { - std::vector<u8> ring_config{0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, - 0xED, 0x34, 0x36, 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, - 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config, output); if (result != DriverResult::Success) { @@ -116,13 +116,13 @@ DriverResult RingConProtocol::ConfigureRing() { } } while (output[14] != 0x5C); - std::vector<u8> ringcon_data{0x04, 0x01, 0x01, 0x02}; + static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02}; result = SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data, output); return result; } -bool RingConProtocol::IsEnabled() { +bool RingConProtocol::IsEnabled() const { return is_enabled; } diff --git a/src/input_common/helpers/joycon_protocol/ringcon.h b/src/input_common/helpers/joycon_protocol/ringcon.h index 0c25de23ee..6e858f3fcb 100644 --- a/src/input_common/helpers/joycon_protocol/ringcon.h +++ b/src/input_common/helpers/joycon_protocol/ringcon.h @@ -17,7 +17,7 @@ namespace InputCommon::Joycon { class RingConProtocol final : private JoyconCommonProtocol { public: - RingConProtocol(std::shared_ptr<JoyconHandle> handle); + explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle); DriverResult EnableRingCon(); @@ -25,7 +25,7 @@ public: DriverResult StartRingconPolling(); - bool IsEnabled(); + bool IsEnabled() const; private: DriverResult IsRingConnected(bool& is_connected); diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp index 17ee388634..fad67a94ba 100644 --- a/src/input_common/helpers/joycon_protocol/rumble.cpp +++ b/src/input_common/helpers/joycon_protocol/rumble.cpp @@ -1,17 +1,20 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> +#include <cmath> + #include "common/logging/log.h" #include "input_common/helpers/joycon_protocol/rumble.h" namespace InputCommon::Joycon { RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle) - : JoyconCommonProtocol(handle) {} + : JoyconCommonProtocol(std::move(handle)) {} DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { LOG_DEBUG(Input, "Enable Rumble"); - const std::vector<u8> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; + const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; std::vector<u8> output; SetBlocking(); const auto result = SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer, output); @@ -20,7 +23,7 @@ DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { } DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) { - std::vector<u8> buffer(sizeof(DefaultVibrationBuffer)); + std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{}; if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) { return SendVibrationReport(DefaultVibrationBuffer); @@ -66,7 +69,7 @@ u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const { /* More information about these values can be found here: * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md */ - constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ + static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ std::pair<f32, int>{0.0f, 0x0}, {0.01f, 0x2}, {0.012f, 0x4}, @@ -183,7 +186,7 @@ u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const { /* More information about these values can be found here: * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md */ - constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ + static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ std::pair<f32, int>{0.0f, 0x0040}, {0.01f, 0x8040}, {0.012f, 0x0041}, diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h index 7d0329f039..6c12b79258 100644 --- a/src/input_common/helpers/joycon_protocol/rumble.h +++ b/src/input_common/helpers/joycon_protocol/rumble.h @@ -17,7 +17,7 @@ namespace InputCommon::Joycon { class RumbleProtocol final : private JoyconCommonProtocol { public: - RumbleProtocol(std::shared_ptr<JoyconHandle> handle); + explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle); DriverResult EnableRumble(bool is_enabled); -- cgit v1.2.3-70-g09d2 From 527dad70976a158e94defc51707347e064a31099 Mon Sep 17 00:00:00 2001 From: german77 <juangerman-13@hotmail.com> Date: Mon, 26 Dec 2022 11:11:01 -0600 Subject: input_common: Use DriverResult on all engines --- src/common/input.h | 46 ++++++++++------------ src/core/hid/emulated_controller.cpp | 10 ++--- src/input_common/drivers/camera.cpp | 4 +- src/input_common/drivers/camera.h | 4 +- src/input_common/drivers/gc_adapter.cpp | 6 +-- src/input_common/drivers/gc_adapter.h | 2 +- src/input_common/drivers/joycon.cpp | 41 +++++++++---------- src/input_common/drivers/joycon.h | 12 +++--- src/input_common/drivers/sdl_driver.cpp | 4 +- src/input_common/drivers/sdl_driver.h | 2 +- src/input_common/drivers/virtual_amiibo.cpp | 4 +- src/input_common/drivers/virtual_amiibo.h | 2 +- src/input_common/helpers/joycon_driver.cpp | 26 ++++++------ src/input_common/helpers/joycon_driver.h | 10 ++--- .../helpers/joycon_protocol/joycon_types.h | 1 + src/input_common/input_engine.h | 19 +++++---- src/input_common/input_poller.cpp | 11 +++--- 17 files changed, 100 insertions(+), 104 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/common/input.h b/src/common/input.h index 1e5ba038d8..d61cd7ca88 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -64,20 +64,19 @@ enum class CameraFormat { None, }; -// Vibration reply from the controller -enum class VibrationError { - None, - NotSupported, - Disabled, +// Different results that can happen from a device request +enum class DriverResult { + Success, + WrongReply, + Timeout, + UnsupportedControllerType, + HandleInUse, + ErrorReadingData, + ErrorWritingData, + NoDeviceDetected, InvalidHandle, - Unknown, -}; - -// Polling mode reply from the controller -enum class PollingError { - None, NotSupported, - InvalidHandle, + Disabled, Unknown, }; @@ -94,13 +93,6 @@ enum class NfcState { Unknown, }; -// Ir camera reply from the controller -enum class CameraError { - None, - NotSupported, - Unknown, -}; - // Hint for amplification curve to be used enum class VibrationAmplificationType { Linear, @@ -336,22 +328,24 @@ class OutputDevice { public: virtual ~OutputDevice() = default; - virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {} + virtual DriverResult SetLED([[maybe_unused]] const LedStatus& led_status) { + return DriverResult::NotSupported; + } - virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { - return VibrationError::NotSupported; + virtual DriverResult SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { + return DriverResult::NotSupported; } virtual bool IsVibrationEnabled() { return false; } - virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { - return PollingError::NotSupported; + virtual DriverResult SetPollingMode([[maybe_unused]] PollingMode polling_mode) { + return DriverResult::NotSupported; } - virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { - return CameraError::NotSupported; + virtual DriverResult SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { + return DriverResult::NotSupported; } virtual NfcState SupportsNfc() const { diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 1ed57f9491..62da5be6ca 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1178,7 +1178,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v .type = type, }; return output_devices[device_index]->SetVibration(status) == - Common::Input::VibrationError::None; + Common::Input::DriverResult::Success; } bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { @@ -1208,8 +1208,8 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); - return virtual_nfc_result == Common::Input::PollingError::None || - mapped_nfc_result == Common::Input::PollingError::None; + return virtual_nfc_result == Common::Input::DriverResult::Success || + mapped_nfc_result == Common::Input::DriverResult::Success; } bool EmulatedController::SetCameraFormat( @@ -1220,13 +1220,13 @@ bool EmulatedController::SetCameraFormat( auto& camera_output_device = output_devices[2]; if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( - camera_format)) == Common::Input::CameraError::None) { + camera_format)) == Common::Input::DriverResult::Success) { return true; } // Fallback to Qt camera if native device doesn't have support return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( - camera_format)) == Common::Input::CameraError::None; + camera_format)) == Common::Input::DriverResult::Success; } Common::ParamPackage EmulatedController::GetRingParam() const { diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp index fad9177dcb..04970f635d 100644 --- a/src/input_common/drivers/camera.cpp +++ b/src/input_common/drivers/camera.cpp @@ -72,11 +72,11 @@ std::size_t Camera::getImageHeight() const { } } -Common::Input::CameraError Camera::SetCameraFormat( +Common::Input::DriverResult Camera::SetCameraFormat( [[maybe_unused]] const PadIdentifier& identifier_, const Common::Input::CameraFormat camera_format) { status.format = camera_format; - return Common::Input::CameraError::None; + return Common::Input::DriverResult::Success; } } // namespace InputCommon diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h index ead3e0fdee..24b27e3258 100644 --- a/src/input_common/drivers/camera.h +++ b/src/input_common/drivers/camera.h @@ -22,8 +22,8 @@ public: std::size_t getImageWidth() const; std::size_t getImageHeight() const; - Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, - Common::Input::CameraFormat camera_format) override; + Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_, + Common::Input::CameraFormat camera_format) override; private: Common::Input::CameraStatus status{}; diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 826fa21097..ecb3e9dc29 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp @@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) { return true; } -Common::Input::VibrationError GCAdapter::SetVibration( +Common::Input::DriverResult GCAdapter::SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; const auto processed_amplitude = @@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration( pads[identifier.port].rumble_amplitude = processed_amplitude; if (!rumble_enabled) { - return Common::Input::VibrationError::Disabled; + return Common::Input::DriverResult::Disabled; } - return Common::Input::VibrationError::None; + return Common::Input::DriverResult::Success; } bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index b5270fd0bb..3c2eb376dc 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h @@ -25,7 +25,7 @@ public: explicit GCAdapter(std::string input_engine_); ~GCAdapter() override; - Common::Input::VibrationError SetVibration( + Common::Input::DriverResult SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; bool IsVibrationEnabled(const PadIdentifier& identifier) override; diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 696a6db393..cf54f1b533 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -233,8 +233,8 @@ bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) { return handle->IsVibrationEnabled(); } -Common::Input::VibrationError Joycons::SetVibration( - const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { +Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier, + const Common::Input::VibrationStatus& vibration) { const Joycon::VibrationValue native_vibration{ .low_amplitude = vibration.low_amplitude, .low_frequency = vibration.low_frequency, @@ -243,32 +243,31 @@ Common::Input::VibrationError Joycons::SetVibration( }; auto handle = GetHandle(identifier); if (handle == nullptr) { - return Common::Input::VibrationError::InvalidHandle; + return Common::Input::DriverResult::InvalidHandle; } handle->SetVibration(native_vibration); - return Common::Input::VibrationError::None; + return Common::Input::DriverResult::Success; } -void Joycons::SetLeds(const PadIdentifier& identifier, const Common::Input::LedStatus& led_status) { +Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier, + const Common::Input::LedStatus& led_status) { auto handle = GetHandle(identifier); if (handle == nullptr) { - return; + return Common::Input::DriverResult::InvalidHandle; } int led_config = led_status.led_1 ? 1 : 0; led_config += led_status.led_2 ? 2 : 0; led_config += led_status.led_3 ? 4 : 0; led_config += led_status.led_4 ? 8 : 0; - const auto result = handle->SetLedConfig(static_cast<u8>(led_config)); - if (result != Joycon::DriverResult::Success) { - LOG_ERROR(Input, "Failed to set led config"); - } + return static_cast<Common::Input::DriverResult>( + handle->SetLedConfig(static_cast<u8>(led_config))); } -Common::Input::CameraError Joycons::SetCameraFormat(const PadIdentifier& identifier_, - Common::Input::CameraFormat camera_format) { - return Common::Input::CameraError::NotSupported; +Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier_, + Common::Input::CameraFormat camera_format) { + return Common::Input::DriverResult::NotSupported; }; Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const { @@ -280,32 +279,30 @@ Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_, return Common::Input::NfcState::NotSupported; }; -Common::Input::PollingError Joycons::SetPollingMode(const PadIdentifier& identifier, +Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) { auto handle = GetHandle(identifier); if (handle == nullptr) { LOG_ERROR(Input, "Invalid handle {}", identifier.port); - return Common::Input::PollingError::InvalidHandle; + return Common::Input::DriverResult::InvalidHandle; } switch (polling_mode) { case Common::Input::PollingMode::NFC: - handle->SetNfcMode(); + return static_cast<Common::Input::DriverResult>(handle->SetNfcMode()); break; case Common::Input::PollingMode::Active: - handle->SetActiveMode(); + return static_cast<Common::Input::DriverResult>(handle->SetActiveMode()); break; case Common::Input::PollingMode::Pasive: - handle->SetPasiveMode(); + return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode()); break; case Common::Input::PollingMode::Ring: - handle->SetRingConMode(); + return static_cast<Common::Input::DriverResult>(handle->SetRingConMode()); break; default: - return Common::Input::PollingError::NotSupported; + return Common::Input::DriverResult::NotSupported; } - - return Common::Input::PollingError::None; } void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index 56c1172701..1a04c19fd3 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -29,20 +29,20 @@ public: ~Joycons(); bool IsVibrationEnabled(const PadIdentifier& identifier) override; - Common::Input::VibrationError SetVibration( + Common::Input::DriverResult SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; - void SetLeds(const PadIdentifier& identifier, - const Common::Input::LedStatus& led_status) override; + Common::Input::DriverResult SetLeds(const PadIdentifier& identifier, + const Common::Input::LedStatus& led_status) override; - Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, - Common::Input::CameraFormat camera_format) override; + 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_, const std::vector<u8>& data) override; - Common::Input::PollingError SetPollingMode( + Common::Input::DriverResult SetPollingMode( const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; /// Used for automapping features diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index c9496a0d86..51a9d8962e 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -545,7 +545,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { return devices; } -Common::Input::VibrationError SDLDriver::SetVibration( +Common::Input::DriverResult SDLDriver::SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { const auto joystick = GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); @@ -579,7 +579,7 @@ Common::Input::VibrationError SDLDriver::SetVibration( .vibration = new_vibration, }); - return Common::Input::VibrationError::None; + return Common::Input::DriverResult::Success; } bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index 366bcc4965..ffde169b35 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -63,7 +63,7 @@ public: bool IsStickInverted(const Common::ParamPackage& params) override; - Common::Input::VibrationError SetVibration( + Common::Input::DriverResult SetVibration( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; bool IsVibrationEnabled(const PadIdentifier& identifier) override; diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 63ffaca67b..29e129d3cc 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -22,7 +22,7 @@ VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move( VirtualAmiibo::~VirtualAmiibo() = default; -Common::Input::PollingError VirtualAmiibo::SetPollingMode( +Common::Input::DriverResult VirtualAmiibo::SetPollingMode( [[maybe_unused]] const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) { polling_mode = polling_mode_; @@ -37,7 +37,7 @@ Common::Input::PollingError VirtualAmiibo::SetPollingMode( } } - return Common::Input::PollingError::None; + return Common::Input::DriverResult::Success; } Common::Input::NfcState VirtualAmiibo::SupportsNfc( diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 0f9dad3338..13cacfc0af 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -36,7 +36,7 @@ public: ~VirtualAmiibo() override; // Sets polling mode to a controller - Common::Input::PollingError SetPollingMode( + Common::Input::DriverResult SetPollingMode( const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 8982a2397f..b00b6110b5 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -459,23 +459,23 @@ SerialNumber JoyconDriver::GetHandleSerialNumber() const { return handle_serial_number; } -void JoyconDriver::SetCallbacks(const Joycon::JoyconCallbacks& callbacks) { +void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) { joycon_poller->SetCallbacks(callbacks); } -Joycon::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, - ControllerType& controller_type) { - static constexpr std::array<std::pair<u32, Joycon::ControllerType>, 4> supported_devices{ - std::pair<u32, Joycon::ControllerType>{0x2006, Joycon::ControllerType::Left}, - {0x2007, Joycon::ControllerType::Right}, - {0x2009, Joycon::ControllerType::Pro}, - {0x200E, Joycon::ControllerType::Grip}, +DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, + ControllerType& controller_type) { + static constexpr std::array<std::pair<u32, ControllerType>, 4> supported_devices{ + std::pair<u32, ControllerType>{0x2006, ControllerType::Left}, + {0x2007, ControllerType::Right}, + {0x2009, ControllerType::Pro}, + {0x200E, ControllerType::Grip}, }; constexpr u16 nintendo_vendor_id = 0x057e; - controller_type = Joycon::ControllerType::None; + controller_type = ControllerType::None; if (device_info->vendor_id != nintendo_vendor_id) { - return Joycon::DriverResult::UnsupportedControllerType; + return DriverResult::UnsupportedControllerType; } for (const auto& [product_id, type] : supported_devices) { @@ -487,10 +487,10 @@ Joycon::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_inf return Joycon::DriverResult::UnsupportedControllerType; } -Joycon::DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info, - Joycon::SerialNumber& serial_number) { +DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info, + SerialNumber& serial_number) { if (device_info->serial_number == nullptr) { - return Joycon::DriverResult::Unknown; + return DriverResult::Unknown; } std::memcpy(&serial_number, device_info->serial_number, 15); return Joycon::DriverResult::Success; diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index c9118ee939..bf38a30090 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -46,15 +46,15 @@ public: DriverResult SetNfcMode(); DriverResult SetRingConMode(); - void SetCallbacks(const Joycon::JoyconCallbacks& callbacks); + void SetCallbacks(const JoyconCallbacks& callbacks); // Returns device type from hidapi handle - static Joycon::DriverResult GetDeviceType(SDL_hid_device_info* device_info, - Joycon::ControllerType& controller_type); + static DriverResult GetDeviceType(SDL_hid_device_info* device_info, + ControllerType& controller_type); // Returns serial number from hidapi handle - static Joycon::DriverResult GetSerialNumber(SDL_hid_device_info* device_info, - Joycon::SerialNumber& serial_number); + static DriverResult GetSerialNumber(SDL_hid_device_info* device_info, + SerialNumber& serial_number); private: struct SupportedFeatures { diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index de512fe639..36c00a8d70 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -284,6 +284,7 @@ enum class DriverResult { NoDeviceDetected, InvalidHandle, NotSupported, + Disabled, Unknown, }; diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 6301c5719a..50b5a3dc8c 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -105,14 +105,17 @@ public: void EndConfiguration(); // Sets a led pattern for a controller - virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier, - [[maybe_unused]] const Common::Input::LedStatus& led_status) {} + virtual Common::Input::DriverResult SetLeds( + [[maybe_unused]] const PadIdentifier& identifier, + [[maybe_unused]] const Common::Input::LedStatus& led_status) { + return Common::Input::DriverResult::NotSupported; + } // Sets rumble to a controller - virtual Common::Input::VibrationError SetVibration( + virtual Common::Input::DriverResult SetVibration( [[maybe_unused]] const PadIdentifier& identifier, [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { - return Common::Input::VibrationError::NotSupported; + return Common::Input::DriverResult::NotSupported; } // Returns true if device supports vibrations @@ -121,17 +124,17 @@ public: } // Sets polling mode to a controller - virtual Common::Input::PollingError SetPollingMode( + virtual Common::Input::DriverResult SetPollingMode( [[maybe_unused]] const PadIdentifier& identifier, [[maybe_unused]] const Common::Input::PollingMode polling_mode) { - return Common::Input::PollingError::NotSupported; + return Common::Input::DriverResult::NotSupported; } // Sets camera format to a controller - virtual Common::Input::CameraError SetCameraFormat( + virtual Common::Input::DriverResult SetCameraFormat( [[maybe_unused]] const PadIdentifier& identifier, [[maybe_unused]] Common::Input::CameraFormat camera_format) { - return Common::Input::CameraError::NotSupported; + return Common::Input::DriverResult::NotSupported; } // Returns success if nfc is supported diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 368ffbdd5a..15cbf7e5f0 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -806,11 +806,11 @@ public: explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) : identifier(identifier_), input_engine(input_engine_) {} - void SetLED(const Common::Input::LedStatus& led_status) override { - input_engine->SetLeds(identifier, led_status); + Common::Input::DriverResult SetLED(const Common::Input::LedStatus& led_status) override { + return input_engine->SetLeds(identifier, led_status); } - Common::Input::VibrationError SetVibration( + Common::Input::DriverResult SetVibration( const Common::Input::VibrationStatus& vibration_status) override { return input_engine->SetVibration(identifier, vibration_status); } @@ -819,11 +819,12 @@ public: return input_engine->IsVibrationEnabled(identifier); } - Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { + Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode) override { return input_engine->SetPollingMode(identifier, polling_mode); } - Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override { + Common::Input::DriverResult SetCameraFormat( + Common::Input::CameraFormat camera_format) override { return input_engine->SetCameraFormat(identifier, camera_format); } -- cgit v1.2.3-70-g09d2 From 459fb2b21337bae60194a2a99ce68c87aaed522d Mon Sep 17 00:00:00 2001 From: Narr the Reg <juangerman-13@hotmail.com> Date: Wed, 28 Dec 2022 15:21:12 -0600 Subject: input_common: Implement joycon ir camera --- src/core/hid/emulated_controller.cpp | 21 +- src/core/hid/emulated_controller.h | 5 +- src/core/hle/service/hid/irs.cpp | 11 + src/input_common/CMakeLists.txt | 2 + src/input_common/drivers/joycon.cpp | 29 +- src/input_common/drivers/joycon.h | 5 +- src/input_common/helpers/joycon_driver.cpp | 55 +++- src/input_common/helpers/joycon_driver.h | 4 + .../helpers/joycon_protocol/common_protocol.cpp | 13 + .../helpers/joycon_protocol/common_protocol.h | 7 + src/input_common/helpers/joycon_protocol/irs.cpp | 300 +++++++++++++++++++++ src/input_common/helpers/joycon_protocol/irs.h | 63 +++++ .../helpers/joycon_protocol/joycon_types.h | 107 +++++++- .../helpers/joycon_protocol/poller.cpp | 6 +- src/input_common/helpers/joycon_protocol/poller.h | 3 +- 15 files changed, 608 insertions(+), 23 deletions(-) create mode 100644 src/input_common/helpers/joycon_protocol/irs.cpp create mode 100644 src/input_common/helpers/joycon_protocol/irs.h (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 915ffa4906..faf9e7c4e5 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -145,7 +145,9 @@ void EmulatedController::LoadDevices() { battery_params[LeftIndex].Set("battery", true); battery_params[RightIndex].Set("battery", true); - camera_params = Common::ParamPackage{"engine:camera,camera:1"}; + camera_params[0] = right_joycon; + camera_params[0].Set("camera", true); + camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; nfc_params[1] = right_joycon; @@ -153,7 +155,7 @@ void EmulatedController::LoadDevices() { output_params[LeftIndex] = left_joycon; output_params[RightIndex] = right_joycon; - output_params[2] = camera_params; + output_params[2] = camera_params[1]; output_params[3] = nfc_params[0]; output_params[LeftIndex].Set("output", true); output_params[RightIndex].Set("output", true); @@ -171,7 +173,7 @@ void EmulatedController::LoadDevices() { std::ranges::transform(battery_params, battery_devices.begin(), Common::Input::CreateInputDevice); std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice); - camera_devices = Common::Input::CreateInputDevice(camera_params); + std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice); std::ranges::transform(ring_params, ring_analog_devices.begin(), Common::Input::CreateInputDevice); std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice); @@ -362,12 +364,15 @@ void EmulatedController::ReloadInput() { motion_devices[index]->ForceUpdate(); } - if (camera_devices) { - camera_devices->SetCallback({ + for (std::size_t index = 0; index < camera_devices.size(); ++index) { + if (!camera_devices[index]) { + continue; + } + camera_devices[index]->SetCallback({ .on_change = [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, }); - camera_devices->ForceUpdate(); + camera_devices[index]->ForceUpdate(); } for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) { @@ -477,7 +482,9 @@ void EmulatedController::UnloadInput() { for (auto& stick : virtual_stick_devices) { stick.reset(); } - camera_devices.reset(); + for (auto& camera : camera_devices) { + camera.reset(); + } for (auto& ring : ring_analog_devices) { ring.reset(); } diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index fb931fc0a8..edebfc15c2 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -39,7 +39,8 @@ using ColorDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; using BatteryDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; +using CameraDevices = + std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; using RingAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; using NfcDevices = @@ -52,7 +53,7 @@ using ControllerMotionParams = std::array<Common::ParamPackage, Settings::Native using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>; using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using CameraParams = Common::ParamPackage; +using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>; using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>; using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>; using OutputParams = std::array<Common::ParamPackage, output_devices_size>; diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 6a3453457f..3c1fa2274f 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -74,6 +74,8 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); + npad_device->SetPollingMode(Common::Input::PollingMode::Active); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -108,6 +110,7 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { auto result = IsIrCameraHandleValid(parameters.camera_handle); if (result.IsSuccess()) { // TODO: Stop Image processor + npad_device->SetPollingMode(Common::Input::PollingMode::Active); result = ResultSuccess; } @@ -139,6 +142,7 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { MakeProcessor<MomentProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(parameters.processor_config); + npad_device->SetPollingMode(Common::Input::PollingMode::IR); } IPC::ResponseBuilder rb{ctx, 2}; @@ -170,6 +174,7 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(parameters.processor_config); + npad_device->SetPollingMode(Common::Input::PollingMode::IR); } IPC::ResponseBuilder rb{ctx, 2}; @@ -219,6 +224,7 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { GetProcessor<ImageTransferProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(parameters.processor_config); image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + npad_device->SetPollingMode(Common::Input::PollingMode::IR); } IPC::ResponseBuilder rb{ctx, 2}; @@ -294,6 +300,7 @@ void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(parameters.processor_config); + npad_device->SetPollingMode(Common::Input::PollingMode::IR); } IPC::ResponseBuilder rb{ctx, 2}; @@ -343,6 +350,7 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { MakeProcessor<PointingProcessor>(camera_handle, device); auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); image_transfer_processor.SetConfig(processor_config); + npad_device->SetPollingMode(Common::Input::PollingMode::IR); } IPC::ResponseBuilder rb{ctx, 2}; @@ -453,6 +461,7 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { GetProcessor<ImageTransferProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(parameters.processor_config); image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + npad_device->SetPollingMode(Common::Input::PollingMode::IR); } IPC::ResponseBuilder rb{ctx, 2}; @@ -479,6 +488,7 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { MakeProcessor<IrLedProcessor>(camera_handle, device); auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); image_transfer_processor.SetConfig(processor_config); + npad_device->SetPollingMode(Common::Input::PollingMode::IR); } IPC::ResponseBuilder rb{ctx, 2}; @@ -504,6 +514,7 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { auto result = IsIrCameraHandleValid(parameters.camera_handle); if (result.IsSuccess()) { // TODO: Stop image processor async + npad_device->SetPollingMode(Common::Input::PollingMode::IR); result = ResultSuccess; } diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 9c901af2ab..e3b627e4ff 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -64,6 +64,8 @@ if (ENABLE_SDL2) helpers/joycon_protocol/generic_functions.cpp helpers/joycon_protocol/generic_functions.h helpers/joycon_protocol/joycon_types.h + helpers/joycon_protocol/irs.cpp + helpers/joycon_protocol/irs.h helpers/joycon_protocol/nfc.cpp helpers/joycon_protocol/nfc.h helpers/joycon_protocol/poller.cpp diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index cf54f1b533..6c03e09537 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -191,6 +191,10 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) { .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) { OnAmiiboUpdate(port, amiibo_data); }}, + .on_camera_data = {[this, port](const std::vector<u8>& camera_data, + Joycon::IrsResolution format) { + OnCameraUpdate(port, camera_data, format); + }}, }; handle->InitializeDevice(); @@ -265,9 +269,14 @@ Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier, handle->SetLedConfig(static_cast<u8>(led_config))); } -Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier_, +Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier, Common::Input::CameraFormat camera_format) { - return Common::Input::DriverResult::NotSupported; + auto handle = GetHandle(identifier); + if (handle == nullptr) { + return Common::Input::DriverResult::InvalidHandle; + } + return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig( + Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format))); }; Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const { @@ -288,18 +297,16 @@ Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identif } switch (polling_mode) { - case Common::Input::PollingMode::NFC: - return static_cast<Common::Input::DriverResult>(handle->SetNfcMode()); - break; case Common::Input::PollingMode::Active: return static_cast<Common::Input::DriverResult>(handle->SetActiveMode()); - break; case Common::Input::PollingMode::Pasive: return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode()); - break; + case Common::Input::PollingMode::IR: + return static_cast<Common::Input::DriverResult>(handle->SetIrMode()); + case Common::Input::PollingMode::NFC: + return static_cast<Common::Input::DriverResult>(handle->SetNfcMode()); case Common::Input::PollingMode::Ring: return static_cast<Common::Input::DriverResult>(handle->SetRingConMode()); - break; default: return Common::Input::DriverResult::NotSupported; } @@ -390,6 +397,12 @@ void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_dat SetNfc(identifier, {nfc_state, amiibo_data}); } +void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, + Joycon::IrsResolution format) { + const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right); + SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data}); +} + std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const { auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) { if (!device) { diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index 1a04c19fd3..f180b74783 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -17,6 +17,7 @@ struct Color; struct MotionData; enum class ControllerType; enum class DriverResult; +enum class IrsResolution; class JoyconDriver; } // namespace InputCommon::Joycon @@ -35,7 +36,7 @@ public: Common::Input::DriverResult SetLeds(const PadIdentifier& identifier, const Common::Input::LedStatus& led_status) override; - Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_, + Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, Common::Input::CameraFormat camera_format) override; Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; @@ -81,6 +82,8 @@ private: const Joycon::MotionData& value); void OnRingConUpdate(f32 ring_data); void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data); + void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, + Joycon::IrsResolution format); /// Returns a JoyconHandle corresponding to a PadIdentifier std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const; diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 8217ba7f61..040832a4ba 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -7,6 +7,7 @@ #include "input_common/helpers/joycon_driver.h" #include "input_common/helpers/joycon_protocol/calibration.h" #include "input_common/helpers/joycon_protocol/generic_functions.h" +#include "input_common/helpers/joycon_protocol/irs.h" #include "input_common/helpers/joycon_protocol/nfc.h" #include "input_common/helpers/joycon_protocol/poller.h" #include "input_common/helpers/joycon_protocol/ringcon.h" @@ -78,6 +79,7 @@ DriverResult JoyconDriver::InitializeDevice() { // Initialize HW Protocols calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle); generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); + irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle); nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle); ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle); rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); @@ -200,10 +202,15 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { .min_value = ring_calibration.min_value, }; + if (irs_protocol->IsEnabled()) { + irs_protocol->RequestImage(buffer); + joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); + } + if (nfc_protocol->IsEnabled()) { if (amiibo_detected) { if (!nfc_protocol->HasAmiibo()) { - joycon_poller->updateAmiibo({}); + joycon_poller->UpdateAmiibo({}); amiibo_detected = false; return; } @@ -213,7 +220,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { std::vector<u8> data(0x21C); const auto result = nfc_protocol->ScanAmiibo(data); if (result == DriverResult::Success) { - joycon_poller->updateAmiibo(data); + joycon_poller->UpdateAmiibo(data); amiibo_detected = true; } } @@ -251,6 +258,20 @@ DriverResult JoyconDriver::SetPollingMode() { generic_protocol->EnableImu(false); } + if (irs_protocol->IsEnabled()) { + irs_protocol->DisableIrs(); + } + + if (irs_enabled && supported_features.irs) { + auto result = irs_protocol->EnableIrs(); + if (result == DriverResult::Success) { + disable_input_thread = false; + return result; + } + irs_protocol->DisableIrs(); + LOG_ERROR(Input, "Error enabling IRS"); + } + if (nfc_protocol->IsEnabled()) { amiibo_detected = false; nfc_protocol->DisableNfc(); @@ -375,12 +396,24 @@ DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { return generic_protocol->SetLedPattern(led_pattern); } +DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) { + std::scoped_lock lock{mutex}; + if (disable_input_thread) { + return DriverResult::HandleInUse; + } + disable_input_thread = true; + const auto result = irs_protocol->SetIrsConfig(mode_, format_); + disable_input_thread = false; + return result; +} + DriverResult JoyconDriver::SetPasiveMode() { std::scoped_lock lock{mutex}; motion_enabled = false; hidbus_enabled = false; nfc_enabled = false; passive_enabled = true; + irs_enabled = false; return SetPollingMode(); } @@ -390,6 +423,22 @@ DriverResult JoyconDriver::SetActiveMode() { hidbus_enabled = false; nfc_enabled = false; passive_enabled = false; + irs_enabled = false; + return SetPollingMode(); +} + +DriverResult JoyconDriver::SetIrMode() { + std::scoped_lock lock{mutex}; + + if (!supported_features.irs) { + return DriverResult::NotSupported; + } + + motion_enabled = false; + hidbus_enabled = false; + nfc_enabled = false; + passive_enabled = false; + irs_enabled = true; return SetPollingMode(); } @@ -404,6 +453,7 @@ DriverResult JoyconDriver::SetNfcMode() { hidbus_enabled = false; nfc_enabled = true; passive_enabled = false; + irs_enabled = false; return SetPollingMode(); } @@ -418,6 +468,7 @@ DriverResult JoyconDriver::SetRingConMode() { hidbus_enabled = true; nfc_enabled = false; passive_enabled = false; + irs_enabled = false; const auto result = SetPollingMode(); diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index 5ff15c7841..61ecf4a6c2 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -13,6 +13,7 @@ namespace InputCommon::Joycon { class CalibrationProtocol; class GenericProtocol; +class IrsProtocol; class NfcProtocol; class JoyconPoller; class RingConProtocol; @@ -41,8 +42,10 @@ public: DriverResult SetVibration(const VibrationValue& vibration); DriverResult SetLedConfig(u8 led_pattern); + DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_); DriverResult SetPasiveMode(); DriverResult SetActiveMode(); + DriverResult SetIrMode(); DriverResult SetNfcMode(); DriverResult SetRingConMode(); @@ -87,6 +90,7 @@ private: // Protocol Features std::unique_ptr<CalibrationProtocol> calibration_protocol; std::unique_ptr<GenericProtocol> generic_protocol; + std::unique_ptr<IrsProtocol> irs_protocol; std::unique_ptr<NfcProtocol> nfc_protocol; std::unique_ptr<JoyconPoller> joycon_poller; std::unique_ptr<RingConProtocol> ring_protocol; diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index a4d08fdafa..a329db1070 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp @@ -120,6 +120,19 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const return DriverResult::Success; } +DriverResult JoyconCommonProtocol::SendMcuCommand(SubCommand sc, std::span<const u8> buffer) { + std::vector<u8> local_buffer(MaxResponseSize); + + local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); + local_buffer[1] = GetCounter(); + local_buffer[10] = static_cast<u8>(sc); + for (std::size_t i = 0; i < buffer.size(); ++i) { + local_buffer[11 + i] = buffer[i]; + } + + return SendData(local_buffer); +} + DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) { std::vector<u8> local_buffer(MaxResponseSize); diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h index a65e4aa76a..2a3feaf598 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.h +++ b/src/input_common/helpers/joycon_protocol/common_protocol.h @@ -74,6 +74,13 @@ public: */ DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, std::vector<u8>& output); + /** + * Sends a mcu command to the device + * @param sc sub command to be send + * @param buffer data to be send + */ + DriverResult SendMcuCommand(SubCommand sc, std::span<const u8> buffer); + /** * Sends vibration data to the joycon * @param buffer data to be send diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp new file mode 100644 index 0000000000..9dfa503c23 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/irs.cpp @@ -0,0 +1,300 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <thread> +#include "common/logging/log.h" +#include "input_common/helpers/joycon_protocol/irs.h" + +namespace InputCommon::Joycon { + +IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle) + : JoyconCommonProtocol(std::move(handle)) {} + +DriverResult IrsProtocol::EnableIrs() { + LOG_INFO(Input, "Enable IRS"); + DriverResult result{DriverResult::Success}; + SetBlocking(); + + if (result == DriverResult::Success) { + result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); + } + if (result == DriverResult::Success) { + result = EnableMCU(true); + } + if (result == DriverResult::Success) { + result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); + } + if (result == DriverResult::Success) { + const MCUConfig config{ + .command = MCUCommand::ConfigureMCU, + .sub_command = MCUSubCommand::SetMCUMode, + .mode = MCUMode::IR, + .crc = {}, + }; + + result = ConfigureMCU(config); + } + if (result == DriverResult::Success) { + result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR); + } + if (result == DriverResult::Success) { + result = ConfigureIrs(); + } + if (result == DriverResult::Success) { + result = WriteRegistersStep1(); + } + if (result == DriverResult::Success) { + result = WriteRegistersStep2(); + } + + is_enabled = true; + + SetNonBlocking(); + return result; +} + +DriverResult IrsProtocol::DisableIrs() { + LOG_DEBUG(Input, "Disable IRS"); + DriverResult result{DriverResult::Success}; + SetBlocking(); + + if (result == DriverResult::Success) { + result = EnableMCU(false); + } + + is_enabled = false; + + SetNonBlocking(); + return result; +} + +DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) { + irs_mode = mode; + switch (format) { + case IrsResolution::Size320x240: + resolution_code = IrsResolutionCode::Size320x240; + fragments = IrsFragments::Size320x240; + resolution = IrsResolution::Size320x240; + break; + case IrsResolution::Size160x120: + resolution_code = IrsResolutionCode::Size160x120; + fragments = IrsFragments::Size160x120; + resolution = IrsResolution::Size160x120; + break; + case IrsResolution::Size80x60: + resolution_code = IrsResolutionCode::Size80x60; + fragments = IrsFragments::Size80x60; + resolution = IrsResolution::Size80x60; + break; + case IrsResolution::Size20x15: + resolution_code = IrsResolutionCode::Size20x15; + fragments = IrsFragments::Size20x15; + resolution = IrsResolution::Size20x15; + break; + case IrsResolution::Size40x30: + default: + resolution_code = IrsResolutionCode::Size40x30; + fragments = IrsFragments::Size40x30; + resolution = IrsResolution::Size40x30; + break; + } + + // Restart feature + if (is_enabled) { + DisableIrs(); + return EnableIrs(); + } + + return DriverResult::Success; +} + +DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) { + const u8 next_packet_fragment = + static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1)); + + if (buffer[0] == 0x31 && buffer[49] == 0x03) { + u8 new_packet_fragment = buffer[52]; + if (new_packet_fragment == next_packet_fragment) { + packet_fragment = next_packet_fragment; + memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300); + + return RequestFrame(packet_fragment); + } + + if (new_packet_fragment == packet_fragment) { + return RequestFrame(packet_fragment); + } + + return ResendFrame(next_packet_fragment); + } + + return RequestFrame(packet_fragment); +} + +DriverResult IrsProtocol::ConfigureIrs() { + LOG_DEBUG(Input, "Configure IRS"); + constexpr std::size_t max_tries = 28; + std::vector<u8> output; + std::size_t tries = 0; + + const IrsConfigure irs_configuration{ + .command = MCUCommand::ConfigureIR, + .sub_command = MCUSubCommand::SetDeviceMode, + .irs_mode = IrsMode::ImageTransfer, + .number_of_fragments = fragments, + .mcu_major_version = 0x0500, + .mcu_minor_version = 0x1800, + .crc = {}, + }; + buf_image.resize((static_cast<u8>(fragments) + 1) * 300); + + std::vector<u8> request_data(sizeof(IrsConfigure)); + memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure)); + request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + do { + const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); + + if (result != DriverResult::Success) { + return result; + } + if (tries++ >= max_tries) { + return DriverResult::WrongReply; + } + } while (output[15] != 0x0b); + + return DriverResult::Success; +} + +DriverResult IrsProtocol::WriteRegistersStep1() { + LOG_DEBUG(Input, "WriteRegistersStep1"); + DriverResult result{DriverResult::Success}; + constexpr std::size_t max_tries = 28; + std::vector<u8> output; + std::size_t tries = 0; + + const IrsWriteRegisters irs_registers{ + .command = MCUCommand::ConfigureIR, + .sub_command = MCUSubCommand::WriteDeviceRegisters, + .number_of_registers = 0x9, + .registers = + { + IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)}, + {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)}, + {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)}, + {IrRegistersAddress::ExposureTime, 0x00}, + {IrRegistersAddress::Leds, static_cast<u8>(leds)}, + {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)}, + {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)}, + {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)}, + {IrRegistersAddress::WhitePixelThreshold, 0xc8}, + }, + .crc = {}, + }; + + std::vector<u8> request_data(sizeof(IrsWriteRegisters)); + memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); + request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + + std::array<u8, 38> mcu_request{0x02}; + mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); + mcu_request[37] = 0xFF; + + if (result != DriverResult::Success) { + return result; + } + + do { + result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); + + // First time we need to set the report mode + if (result == DriverResult::Success && tries == 0) { + result = SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); + } + if (result == DriverResult::Success && tries == 0) { + GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output); + } + + if (result != DriverResult::Success) { + return result; + } + if (tries++ >= max_tries) { + return DriverResult::WrongReply; + } + } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23); + + return DriverResult::Success; +} + +DriverResult IrsProtocol::WriteRegistersStep2() { + LOG_DEBUG(Input, "WriteRegistersStep2"); + constexpr std::size_t max_tries = 28; + std::vector<u8> output; + std::size_t tries = 0; + + const IrsWriteRegisters irs_registers{ + .command = MCUCommand::ConfigureIR, + .sub_command = MCUSubCommand::WriteDeviceRegisters, + .number_of_registers = 0x8, + .registers = + { + IrsRegister{IrRegistersAddress::LedIntensitiyMSB, + static_cast<u8>(led_intensity >> 8)}, + {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)}, + {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)}, + {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)}, + {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)}, + {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)}, + {IrRegistersAddress::UpdateTime, 0x2d}, + {IrRegistersAddress::FinalizeConfig, 0x01}, + }, + .crc = {}, + }; + + std::vector<u8> request_data(sizeof(IrsWriteRegisters)); + memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); + request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + do { + const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); + + if (result != DriverResult::Success) { + return result; + } + if (tries++ >= max_tries) { + return DriverResult::WrongReply; + } + } while (output[15] != 0x13 && output[15] != 0x23); + + return DriverResult::Success; +} + +DriverResult IrsProtocol::RequestFrame(u8 frame) { + std::array<u8, 38> mcu_request{}; + mcu_request[3] = frame; + mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); + mcu_request[37] = 0xFF; + return SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); +} + +DriverResult IrsProtocol::ResendFrame(u8 frame) { + std::array<u8, 38> mcu_request{}; + mcu_request[1] = 0x1; + mcu_request[2] = frame; + mcu_request[3] = 0x0; + mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); + mcu_request[37] = 0xFF; + return SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); +} + +std::vector<u8> IrsProtocol::GetImage() const { + return buf_image; +} + +IrsResolution IrsProtocol::GetIrsFormat() const { + return resolution; +} + +bool IrsProtocol::IsEnabled() const { + return is_enabled; +} + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h new file mode 100644 index 0000000000..76dfa02ea5 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/irs.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse +// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c +// https://github.com/CTCaer/jc_toolkit +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + +#pragma once + +#include <vector> + +#include "input_common/helpers/joycon_protocol/common_protocol.h" +#include "input_common/helpers/joycon_protocol/joycon_types.h" + +namespace InputCommon::Joycon { + +class IrsProtocol final : private JoyconCommonProtocol { +public: + explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle); + + DriverResult EnableIrs(); + + DriverResult DisableIrs(); + + DriverResult SetIrsConfig(IrsMode mode, IrsResolution format); + + DriverResult RequestImage(std::span<u8> buffer); + + std::vector<u8> GetImage() const; + + IrsResolution GetIrsFormat() const; + + bool IsEnabled() const; + +private: + DriverResult ConfigureIrs(); + + DriverResult WriteRegistersStep1(); + DriverResult WriteRegistersStep2(); + + DriverResult RequestFrame(u8 frame); + DriverResult ResendFrame(u8 frame); + + IrsMode irs_mode{IrsMode::ImageTransfer}; + IrsResolution resolution{IrsResolution::Size40x30}; + IrsResolutionCode resolution_code{IrsResolutionCode::Size40x30}; + IrsFragments fragments{IrsFragments::Size40x30}; + IrLeds leds{IrLeds::BrightAndDim}; + IrExLedFilter led_filter{IrExLedFilter::Enabled}; + IrImageFlip image_flip{IrImageFlip::Normal}; + u8 digital_gain{0x01}; + u16 exposure{0x2490}; + u16 led_intensity{0x0f10}; + u32 denoise{0x012344}; + + u8 packet_fragment{}; + std::vector<u8> buf_image; // 8bpp greyscale image. + + bool is_enabled{}; +}; + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 36c00a8d70..273c8d07d3 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -18,7 +18,7 @@ namespace InputCommon::Joycon { constexpr u32 MaxErrorCount = 50; -constexpr u32 MaxBufferSize = 60; +constexpr u32 MaxBufferSize = 368; constexpr u32 MaxResponseSize = 49; constexpr u32 MaxSubCommandResponseSize = 64; constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40}; @@ -273,6 +273,80 @@ enum class NFCTagType : u8 { Ntag215 = 0x01, }; +enum class IrsMode : u8 { + None = 0x02, + Moment = 0x03, + Dpd = 0x04, + Clustering = 0x06, + ImageTransfer = 0x07, + Silhouette = 0x08, + TeraImage = 0x09, + SilhouetteTeraImage = 0x0A, +}; + +enum class IrsResolution { + Size320x240, + Size160x120, + Size80x60, + Size40x30, + Size20x15, + None, +}; + +enum class IrsResolutionCode : u8 { + Size320x240 = 0x00, // Full pixel array + Size160x120 = 0x50, // Sensor Binning [2 X 2] + Size80x60 = 0x64, // Sensor Binning [4 x 2] and Skipping [1 x 2] + Size40x30 = 0x69, // Sensor Binning [4 x 2] and Skipping [2 x 4] + Size20x15 = 0x6A, // Sensor Binning [4 x 2] and Skipping [4 x 4] +}; + +// Size of image divided by 300 +enum class IrsFragments : u8 { + Size20x15 = 0x00, + Size40x30 = 0x03, + Size80x60 = 0x0f, + Size160x120 = 0x3f, + Size320x240 = 0xFF, +}; + +enum class IrLeds : u8 { + BrightAndDim = 0x00, + Bright = 0x20, + Dim = 0x10, + None = 0x30, +}; + +enum class IrExLedFilter : u8 { + Disabled = 0x00, + Enabled = 0x03, +}; + +enum class IrImageFlip : u8 { + Normal = 0x00, + Inverted = 0x02, +}; + +enum class IrRegistersAddress : u16 { + UpdateTime = 0x0400, + FinalizeConfig = 0x0700, + LedFilter = 0x0e00, + Leds = 0x1000, + LedIntensitiyMSB = 0x1100, + LedIntensitiyLSB = 0x1200, + ImageFlip = 0x2d00, + Resolution = 0x2e00, + DigitalGainLSB = 0x2e01, + DigitalGainMSB = 0x2f01, + ExposureLSB = 0x3001, + ExposureMSB = 0x3101, + ExposureTime = 0x3201, + WhitePixelThreshold = 0x4301, + DenoiseSmoothing = 0x6701, + DenoiseEdge = 0x6801, + DenoiseColor = 0x6901, +}; + enum class DriverResult { Success, WrongReply, @@ -456,6 +530,36 @@ struct NFCRequestState { }; static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); +struct IrsConfigure { + MCUCommand command; + MCUSubCommand sub_command; + IrsMode irs_mode; + IrsFragments number_of_fragments; + u16 mcu_major_version; + u16 mcu_minor_version; + INSERT_PADDING_BYTES(0x1D); + u8 crc; +}; +static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size"); + +#pragma pack(push, 1) +struct IrsRegister { + IrRegistersAddress address; + u8 value; +}; +static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size"); + +struct IrsWriteRegisters { + MCUCommand command; + MCUSubCommand sub_command; + u8 number_of_registers; + std::array<IrsRegister, 9> registers; + INSERT_PADDING_BYTES(0x7); + u8 crc; +}; +static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size"); +#pragma pack(pop) + struct FirmwareVersion { u8 major; u8 minor; @@ -490,6 +594,7 @@ struct JoyconCallbacks { 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 std::vector<u8>&, IrsResolution)> on_camera_data; }; } // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index fd05d98f38..940b20b7f0 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -74,10 +74,14 @@ void JoyconPoller::UpdateColor(const Color& color) { callbacks.on_color_data(color); } -void JoyconPoller::updateAmiibo(const std::vector<u8>& amiibo_data) { +void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { callbacks.on_amiibo_data(amiibo_data); } +void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { + callbacks.on_camera_data(camera_data, format); +} + void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) { float normalized_value = static_cast<float>(value - ring_status.default_value); if (normalized_value > 0) { diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h index c40fc7bca8..354d41dad3 100644 --- a/src/input_common/helpers/joycon_protocol/poller.h +++ b/src/input_common/helpers/joycon_protocol/poller.h @@ -36,7 +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 UpdateAmiibo(const std::vector<u8>& amiibo_data); + void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); private: void UpdateActiveLeftPadInput(const InputReportActive& input, -- cgit v1.2.3-70-g09d2 From b40aefb39ea8b4259acdbe0616790c2234d9b9ef Mon Sep 17 00:00:00 2001 From: german77 <juangerman-13@hotmail.com> Date: Sun, 8 Jan 2023 21:37:13 -0600 Subject: input_common: Drop Pro controller support from custom driver --- src/input_common/drivers/joycon.cpp | 36 ------------------------------ src/input_common/drivers/joycon.h | 1 - src/input_common/drivers/sdl_driver.cpp | 6 ++--- src/input_common/helpers/joycon_driver.cpp | 4 +--- 4 files changed, 4 insertions(+), 43 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 6c03e09537..fff886ca8a 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -44,12 +44,6 @@ void Joycons::Reset() { } device->Stop(); } - for (const auto& device : pro_joycons) { - if (!device) { - continue; - } - device->Stop(); - } SDL_hid_exit(); } @@ -65,11 +59,6 @@ void Joycons::Setup() { PreSetController(GetIdentifier(port, Joycon::ControllerType::Right)); device = std::make_shared<Joycon::JoyconDriver>(port++); } - port = 0; - for (auto& device : pro_joycons) { - PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro)); - device = std::make_shared<Joycon::JoyconDriver>(port++); - } if (!scan_thread_running) { scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); }); @@ -141,14 +130,6 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { } } break; - case Joycon::ControllerType::Pro: - case Joycon::ControllerType::Grip: - for (const auto& device : pro_joycons) { - if (is_handle_identical(device)) { - return false; - } - } - break; default: return false; } @@ -219,13 +200,6 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle( } } } - if (type == Joycon::ControllerType::Pro || type == Joycon::ControllerType::Grip) { - for (const auto& device : pro_joycons) { - if (!device->IsConnected()) { - return device; - } - } - } return nullptr; } @@ -431,13 +405,6 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifie } } } - if (type == Joycon::ControllerType::Pro || type == Joycon::ControllerType::Grip) { - for (const auto& device : pro_joycons) { - if (is_handle_active(device)) { - return device; - } - } - } return nullptr; } @@ -475,9 +442,6 @@ std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { for (const auto& controller : right_joycons) { add_entry(controller); } - for (const auto& controller : pro_joycons) { - add_entry(controller); - } // List dual joycon pairs for (std::size_t i = 0; i < MaxSupportedControllers; i++) { diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index f180b74783..f5cc787db0 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -104,7 +104,6 @@ private: // Joycon types are split by type to ease supporting dualjoycon configurations std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{}; std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{}; - std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> pro_joycons{}; }; } // namespace InputCommon diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 51a9d8962e..e915ec0908 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -319,7 +319,8 @@ void SDLDriver::InitJoystick(int joystick_index) { const auto guid = GetGUID(sdl_joystick); if (Settings::values.enable_joycon_driver) { - if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e) { + if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && + (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) { LOG_ERROR(Input, "Device black listed {}", joystick_index); SDL_JoystickClose(sdl_joystick); return; @@ -451,11 +452,10 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled if (Settings::values.enable_joycon_driver) { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); } else { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); } + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native // driver on Linux. diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index e8aef028af..5525723435 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -540,11 +540,9 @@ void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) { DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, ControllerType& controller_type) { - static constexpr std::array<std::pair<u32, ControllerType>, 4> supported_devices{ + static constexpr std::array<std::pair<u32, ControllerType>, 2> supported_devices{ std::pair<u32, ControllerType>{0x2006, ControllerType::Left}, {0x2007, ControllerType::Right}, - {0x2009, ControllerType::Pro}, - {0x200E, ControllerType::Grip}, }; constexpr u16 nintendo_vendor_id = 0x057e; -- cgit v1.2.3-70-g09d2 From 340f15d1fa79594dbe12a6e19140ba012751b533 Mon Sep 17 00:00:00 2001 From: german77 <juangerman-13@hotmail.com> Date: Fri, 13 Jan 2023 23:29:05 -0600 Subject: input_common: Address byte review --- src/input_common/drivers/joycon.cpp | 50 +++--- src/input_common/drivers/joycon.h | 1 - src/input_common/drivers/sdl_driver.cpp | 2 +- src/input_common/helpers/joycon_driver.cpp | 8 +- src/input_common/helpers/joycon_driver.h | 3 +- .../helpers/joycon_protocol/calibration.cpp | 9 +- .../helpers/joycon_protocol/common_protocol.cpp | 24 +-- .../helpers/joycon_protocol/common_protocol.h | 22 ++- .../helpers/joycon_protocol/generic_functions.cpp | 54 ++---- src/input_common/helpers/joycon_protocol/irs.cpp | 18 +- .../helpers/joycon_protocol/joycon_types.h | 12 ++ src/input_common/helpers/joycon_protocol/nfc.cpp | 183 ++++++++++----------- src/input_common/helpers/joycon_protocol/nfc.h | 4 +- .../helpers/joycon_protocol/poller.cpp | 18 +- .../helpers/joycon_protocol/ringcon.cpp | 36 ++-- .../helpers/joycon_protocol/rumble.cpp | 19 +-- 16 files changed, 220 insertions(+), 243 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index fff886ca8a..1582def13c 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -60,15 +60,12 @@ void Joycons::Setup() { device = std::make_shared<Joycon::JoyconDriver>(port++); } - if (!scan_thread_running) { - scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); }); - } + scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); }); } void Joycons::ScanThread(std::stop_token stop_token) { constexpr u16 nintendo_vendor_id = 0x057e; - Common::SetCurrentThreadName("yuzu:input:JoyconScanThread"); - scan_thread_running = true; + Common::SetCurrentThreadName("JoyconScanThread"); while (!stop_token.stop_requested()) { SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0); SDL_hid_device_info* cur_dev = devs; @@ -82,9 +79,9 @@ void Joycons::ScanThread(std::stop_token stop_token) { cur_dev = cur_dev->next; } + SDL_hid_free_enumeration(devs); std::this_thread::sleep_for(std::chrono::seconds(5)); } - scan_thread_running = false; } bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { @@ -185,19 +182,19 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) { std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle( Joycon::ControllerType type) const { - if (type == Joycon::ControllerType::Left) { - for (const auto& device : left_joycons) { - if (!device->IsConnected()) { - return device; - } + const auto unconnected_device = + std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); }); + if (unconnected_device != left_joycons.end()) { + return *unconnected_device; } } if (type == Joycon::ControllerType::Right) { - for (const auto& device : right_joycons) { - if (!device->IsConnected()) { - return device; - } + const auto unconnected_device = std::ranges::find_if( + right_joycons, [](auto& device) { return !device->IsConnected(); }); + + if (unconnected_device != right_joycons.end()) { + return *unconnected_device; } } return nullptr; @@ -391,20 +388,25 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifie return false; }; const auto type = static_cast<Joycon::ControllerType>(identifier.pad); + if (type == Joycon::ControllerType::Left) { - for (const auto& device : left_joycons) { - if (is_handle_active(device)) { - return device; - } + const auto matching_device = std::ranges::find_if( + left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); }); + + if (matching_device != left_joycons.end()) { + return *matching_device; } } + if (type == Joycon::ControllerType::Right) { - for (const auto& device : right_joycons) { - if (is_handle_active(device)) { - return device; - } + const auto matching_device = std::ranges::find_if( + right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); }); + + if (matching_device != right_joycons.end()) { + return *matching_device; } } + return nullptr; } @@ -676,7 +678,7 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const { case Joycon::ControllerType::Dual: return "Dual Joycon"; default: - return "Unknow Joycon"; + return "Unknown Joycon"; } } } // namespace InputCommon diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index f5cc787db0..6d2e2ec786 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -99,7 +99,6 @@ private: std::string JoyconName(Joycon::ControllerType type) const; std::jthread scan_thread; - bool scan_thread_running{}; // Joycon types are split by type to ease supporting dualjoycon configurations std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{}; diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index e915ec0908..a0103edde2 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -321,7 +321,7 @@ void SDLDriver::InitJoystick(int joystick_index) { if (Settings::values.enable_joycon_driver) { if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) { - LOG_ERROR(Input, "Device black listed {}", joystick_index); + LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index); SDL_JoystickClose(sdl_joystick); return; } diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 5525723435..4159e5717a 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -123,7 +123,7 @@ DriverResult JoyconDriver::InitializeDevice() { } void JoyconDriver::InputThread(std::stop_token stop_token) { - LOG_INFO(Input, "JC Adapter input thread started"); + LOG_INFO(Input, "Joycon Adapter input thread started"); Common::SetCurrentThreadName("JoyconInput"); input_thread_running = true; @@ -157,7 +157,7 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { is_connected = false; input_thread_running = false; - LOG_INFO(Input, "JC Adapter input thread stopped"); + LOG_INFO(Input, "Joycon Adapter input thread stopped"); } void JoyconDriver::OnNewData(std::span<u8> buffer) { @@ -349,7 +349,7 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() { } bool JoyconDriver::IsInputThreadValid() const { - if (!is_connected) { + if (!is_connected.load()) { return false; } if (hidapi_handle->handle == nullptr) { @@ -491,7 +491,7 @@ DriverResult JoyconDriver::SetRingConMode() { bool JoyconDriver::IsConnected() const { std::scoped_lock lock{mutex}; - return is_connected; + return is_connected.load(); } bool JoyconDriver::IsVibrationEnabled() const { diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index e8e65e1331..c1e189fa52 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -3,6 +3,7 @@ #pragma once +#include <atomic> #include <functional> #include <mutex> #include <span> @@ -97,7 +98,7 @@ private: std::unique_ptr<RumbleProtocol> rumble_protocol; // Connection status - bool is_connected{}; + std::atomic<bool> is_connected{}; u64 delta_time; std::size_t error_counter{}; std::shared_ptr<JoyconHandle> hidapi_handle; diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp index cd30ab8698..f6e7e97d59 100644 --- a/src/input_common/helpers/joycon_protocol/calibration.cpp +++ b/src/input_common/helpers/joycon_protocol/calibration.cpp @@ -12,10 +12,10 @@ CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle) : JoyconCommonProtocol(std::move(handle)) {} DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { + ScopedSetBlocking sb(this); std::vector<u8> buffer; DriverResult result{DriverResult::Success}; calibration = {}; - SetBlocking(); result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer); @@ -44,15 +44,14 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration // Set a valid default calibration if data is missing ValidateCalibration(calibration); - SetNonBlocking(); return result; } DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { + ScopedSetBlocking sb(this); std::vector<u8> buffer; DriverResult result{DriverResult::Success}; calibration = {}; - SetBlocking(); result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer); @@ -81,15 +80,14 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio // Set a valid default calibration if data is missing ValidateCalibration(calibration); - SetNonBlocking(); return result; } DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { + ScopedSetBlocking sb(this); std::vector<u8> buffer; DriverResult result{DriverResult::Success}; calibration = {}; - SetBlocking(); result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer); @@ -124,7 +122,6 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati ValidateCalibration(calibration); - SetNonBlocking(); return result; } diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index 153a3908c6..417d0dcc50 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp @@ -58,9 +58,8 @@ DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device } DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) { - const std::vector<u8> buffer{static_cast<u8>(report_mode)}; - std::vector<u8> output; - return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer, output); + const std::array<u8, 1> buffer{static_cast<u8>(report_mode)}; + return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); } DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) { @@ -120,7 +119,12 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const return DriverResult::Success; } -DriverResult JoyconCommonProtocol::SendMcuCommand(SubCommand sc, std::span<const u8> buffer) { +DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { + std::vector<u8> output; + return SendSubCommand(sc, buffer, output); +} + +DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) { std::vector<u8> local_buffer(MaxResponseSize); local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); @@ -147,7 +151,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) { constexpr std::size_t MaxTries = 10; std::size_t tries = 0; - std::vector<u8> buffer = {0x00, 0x00, 0x00, 0x00, size}; + std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size}; std::vector<u8> local_buffer(size + 20); buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); @@ -169,10 +173,8 @@ DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8 } DriverResult JoyconCommonProtocol::EnableMCU(bool enable) { - std::vector<u8> output; - - const std::vector<u8> mcu_state{static_cast<u8>(enable ? 1 : 0)}; - const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state, output); + const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)}; + const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); if (result != DriverResult::Success) { LOG_ERROR(Input, "SendMCUData failed with error {}", result); @@ -183,13 +185,11 @@ DriverResult JoyconCommonProtocol::EnableMCU(bool enable) { DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) { LOG_DEBUG(Input, "ConfigureMCU"); - std::vector<u8> output; - std::array<u8, sizeof(MCUConfig)> config_buffer; memcpy(config_buffer.data(), &config, sizeof(MCUConfig)); config_buffer[37] = CalculateMCU_CRC8(config_buffer.data() + 1, 36); - const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer, output); + const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); if (result != DriverResult::Success) { LOG_ERROR(Input, "Set MCU config failed with error {}", result); diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h index 2a3feaf598..903bcf4025 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.h +++ b/src/input_common/helpers/joycon_protocol/common_protocol.h @@ -74,12 +74,19 @@ public: */ DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, std::vector<u8>& output); + /** + * Sends a sub command to the device and waits for it's reply and ignores the output + * @param sc sub command to be send + * @param buffer data to be send + */ + DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer); + /** * Sends a mcu command to the device * @param sc sub command to be send * @param buffer data to be send */ - DriverResult SendMcuCommand(SubCommand sc, std::span<const u8> buffer); + DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer); /** * Sends vibration data to the joycon @@ -150,4 +157,17 @@ private: std::shared_ptr<JoyconHandle> hidapi_handle; }; +class ScopedSetBlocking { +public: + explicit ScopedSetBlocking(JoyconCommonProtocol* self) : m_self{self} { + m_self->SetBlocking(); + } + + ~ScopedSetBlocking() { + m_self->SetNonBlocking(); + } + +private: + JoyconCommonProtocol* m_self{}; +}; } // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp index cbd9ff4f8b..52bb8b61aa 100644 --- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp +++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp @@ -10,22 +10,18 @@ GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle) : JoyconCommonProtocol(std::move(handle)) {} DriverResult GenericProtocol::EnablePassiveMode() { - SetBlocking(); - const auto result = SetReportMode(ReportMode::SIMPLE_HID_MODE); - SetNonBlocking(); - return result; + ScopedSetBlocking sb(this); + return SetReportMode(ReportMode::SIMPLE_HID_MODE); } DriverResult GenericProtocol::EnableActiveMode() { - SetBlocking(); - const auto result = SetReportMode(ReportMode::STANDARD_FULL_60HZ); - SetNonBlocking(); - return result; + ScopedSetBlocking sb(this); + return SetReportMode(ReportMode::STANDARD_FULL_60HZ); } DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { + ScopedSetBlocking sb(this); std::vector<u8> output; - SetBlocking(); const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); @@ -34,7 +30,6 @@ DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { memcpy(&device_info, output.data(), sizeof(DeviceInfo)); } - SetNonBlocking(); return result; } @@ -43,36 +38,30 @@ DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) } DriverResult GenericProtocol::EnableImu(bool enable) { + ScopedSetBlocking sb(this); const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; - std::vector<u8> output; - SetBlocking(); - const auto result = SendSubCommand(SubCommand::ENABLE_IMU, buffer, output); - SetNonBlocking(); - return result; + return SendSubCommand(SubCommand::ENABLE_IMU, buffer); } DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, AccelerometerSensitivity asen, AccelerometerPerformance afrec) { + ScopedSetBlocking sb(this); const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen), static_cast<u8>(gfrec), static_cast<u8>(afrec)}; - std::vector<u8> output; - SetBlocking(); - const auto result = SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer, output); - SetNonBlocking(); - return result; + return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer); } DriverResult GenericProtocol::GetBattery(u32& battery_level) { + // This function is meant to request the high resolution battery status battery_level = 0; return DriverResult::NotSupported; } DriverResult GenericProtocol::GetColor(Color& color) { + ScopedSetBlocking sb(this); std::vector<u8> buffer; - SetBlocking(); const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); - SetNonBlocking(); color = {}; if (result == DriverResult::Success) { @@ -86,10 +75,9 @@ DriverResult GenericProtocol::GetColor(Color& color) { } DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { + ScopedSetBlocking sb(this); std::vector<u8> buffer; - SetBlocking(); const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); - SetNonBlocking(); serial_number = {}; if (result == DriverResult::Success) { @@ -115,14 +103,9 @@ DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) { } DriverResult GenericProtocol::SetHomeLight() { + ScopedSetBlocking sb(this); static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00}; - std::vector<u8> output; - SetBlocking(); - - const auto result = SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer, output); - - SetNonBlocking(); - return result; + return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer); } DriverResult GenericProtocol::SetLedBusy() { @@ -130,14 +113,9 @@ DriverResult GenericProtocol::SetLedBusy() { } DriverResult GenericProtocol::SetLedPattern(u8 leds) { + ScopedSetBlocking sb(this); const std::array<u8, 1> buffer{leds}; - std::vector<u8> output; - SetBlocking(); - - const auto result = SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer, output); - - SetNonBlocking(); - return result; + return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer); } DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) { diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp index 9dfa503c23..09e17bc5b6 100644 --- a/src/input_common/helpers/joycon_protocol/irs.cpp +++ b/src/input_common/helpers/joycon_protocol/irs.cpp @@ -12,8 +12,8 @@ IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle) DriverResult IrsProtocol::EnableIrs() { LOG_INFO(Input, "Enable IRS"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; - SetBlocking(); if (result == DriverResult::Success) { result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); @@ -49,14 +49,13 @@ DriverResult IrsProtocol::EnableIrs() { is_enabled = true; - SetNonBlocking(); return result; } DriverResult IrsProtocol::DisableIrs() { LOG_DEBUG(Input, "Disable IRS"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; - SetBlocking(); if (result == DriverResult::Success) { result = EnableMCU(false); @@ -64,7 +63,6 @@ DriverResult IrsProtocol::DisableIrs() { is_enabled = false; - SetNonBlocking(); return result; } @@ -148,7 +146,7 @@ DriverResult IrsProtocol::ConfigureIrs() { }; buf_image.resize((static_cast<u8>(fragments) + 1) * 300); - std::vector<u8> request_data(sizeof(IrsConfigure)); + std::array<u8, sizeof(IrsConfigure)> request_data{}; memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure)); request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); do { @@ -191,7 +189,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() { .crc = {}, }; - std::vector<u8> request_data(sizeof(IrsWriteRegisters)); + std::array<u8, sizeof(IrsWriteRegisters)> request_data{}; memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); @@ -208,7 +206,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() { // First time we need to set the report mode if (result == DriverResult::Success && tries == 0) { - result = SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); + result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); } if (result == DriverResult::Success && tries == 0) { GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output); @@ -250,7 +248,7 @@ DriverResult IrsProtocol::WriteRegistersStep2() { .crc = {}, }; - std::vector<u8> request_data(sizeof(IrsWriteRegisters)); + std::array<u8, sizeof(IrsWriteRegisters)> request_data{}; memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); do { @@ -272,7 +270,7 @@ DriverResult IrsProtocol::RequestFrame(u8 frame) { mcu_request[3] = frame; mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); mcu_request[37] = 0xFF; - return SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); + return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); } DriverResult IrsProtocol::ResendFrame(u8 frame) { @@ -282,7 +280,7 @@ DriverResult IrsProtocol::ResendFrame(u8 frame) { mcu_request[3] = 0x0; mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); mcu_request[37] = 0xFF; - return SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); + return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); } std::vector<u8> IrsProtocol::GetImage() const { diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 273c8d07d3..e2d47349f0 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -273,6 +273,18 @@ enum class NFCTagType : u8 { Ntag215 = 0x01, }; +enum class NFCPages { + Block0 = 0, + Block45 = 45, + Block135 = 135, + Block231 = 231, +}; + +enum class NFCStatus : u8 { + LastPackage = 0x04, + TagLost = 0x07, +}; + enum class IrsMode : u8 { None = 0x02, Moment = 0x03, diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index 8755e310b8..5c0f717228 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -12,8 +12,8 @@ NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle) DriverResult NfcProtocol::EnableNfc() { LOG_INFO(Input, "Enable NFC"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; - SetBlocking(); if (result == DriverResult::Success) { result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); @@ -35,14 +35,13 @@ DriverResult NfcProtocol::EnableNfc() { result = ConfigureMCU(config); } - SetNonBlocking(); return result; } DriverResult NfcProtocol::DisableNfc() { LOG_DEBUG(Input, "Disable NFC"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; - SetBlocking(); if (result == DriverResult::Success) { result = EnableMCU(false); @@ -50,15 +49,14 @@ DriverResult NfcProtocol::DisableNfc() { is_enabled = false; - SetNonBlocking(); return result; } DriverResult NfcProtocol::StartNFCPollingMode() { LOG_DEBUG(Input, "Start NFC pooling Mode"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; TagFoundData tag_data{}; - SetBlocking(); if (result == DriverResult::Success) { result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); @@ -70,15 +68,14 @@ DriverResult NfcProtocol::StartNFCPollingMode() { is_enabled = true; } - SetNonBlocking(); return result; } DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { LOG_DEBUG(Input, "Start NFC pooling Mode"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; TagFoundData tag_data{}; - SetBlocking(); if (result == DriverResult::Success) { result = StartPolling(tag_data); @@ -96,20 +93,18 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { result = GetAmiiboData(data); } - SetNonBlocking(); return result; } bool NfcProtocol::HasAmiibo() { + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; TagFoundData tag_data{}; - SetBlocking(); if (result == DriverResult::Success) { result = StartPolling(tag_data); } - SetNonBlocking(); return result == DriverResult::Success; } @@ -169,55 +164,53 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string); tries = 0; - std::size_t ntag_pages = 0; + NFCPages ntag_pages = NFCPages::Block0; // Read Tag data -loop1: while (true) { auto result = SendReadAmiiboRequest(output, ntag_pages); + const auto mcu_report = static_cast<MCUReport>(output[49]); + const auto nfc_status = static_cast<NFCStatus>(output[56]); - int attempt = 0; - while (1) { - if (attempt != 0) { - result = GetMCUDataResponse(ReportMode::NFC_IR_MODE_60HZ, output); + if (result != DriverResult::Success) { + return result; + } + + if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && + nfc_status == NFCStatus::TagLost) { + return DriverResult::ErrorReadingData; + } + + if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07 && output[52] == 0x01) { + if (data.type != 2) { + continue; } - if ((output[49] == 0x3a || output[49] == 0x2a) && output[56] == 0x07) { + switch (output[74]) { + case 0: + ntag_pages = NFCPages::Block135; + break; + case 3: + ntag_pages = NFCPages::Block45; + break; + case 4: + ntag_pages = NFCPages::Block231; + break; + default: return DriverResult::ErrorReadingData; } - if (output[49] == 0x3a && output[51] == 0x07 && output[52] == 0x01) { - if (data.type != 2) { - goto loop1; - } - switch (output[74]) { - case 0: - ntag_pages = 135; - break; - case 3: - ntag_pages = 45; - break; - case 4: - ntag_pages = 231; - break; - default: - return DriverResult::ErrorReadingData; - } - goto loop1; - } - if (output[49] == 0x2a && output[56] == 0x04) { - // finished - SendStopPollingRequest(output); - return DriverResult::Success; - } - if (output[49] == 0x2a) { - goto loop1; - } - if (attempt++ > 6) { - goto loop1; - } + continue; } - if (result != DriverResult::Success) { - return result; + if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { + // finished + SendStopPollingRequest(output); + return DriverResult::Success; + } + + // Ignore other state reports + if (mcu_report == MCUReport::NFCState) { + continue; } + if (tries++ > timeout_limit) { return DriverResult::Timeout; } @@ -231,47 +224,44 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { std::vector<u8> output; std::size_t tries = 0; - std::size_t ntag_pages = 135; + NFCPages ntag_pages = NFCPages::Block135; std::size_t ntag_buffer_pos = 0; // Read Tag data -loop1: while (true) { auto result = SendReadAmiiboRequest(output, ntag_pages); + const auto mcu_report = static_cast<MCUReport>(output[49]); + const auto nfc_status = static_cast<NFCStatus>(output[56]); - int attempt = 0; - while (1) { - if (attempt != 0) { - result = GetMCUDataResponse(ReportMode::NFC_IR_MODE_60HZ, output); - } - if ((output[49] == 0x3a || output[49] == 0x2a) && output[56] == 0x07) { - return DriverResult::ErrorReadingData; - } - if (output[49] == 0x3a && output[51] == 0x07) { - std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; - if (output[52] == 0x01) { - memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, - payload_size - 60); - ntag_buffer_pos += payload_size - 60; - } else { - memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size); - } - goto loop1; - } - if (output[49] == 0x2a && output[56] == 0x04) { - LOG_INFO(Input, "Finished reading amiibo"); - return DriverResult::Success; - } - if (output[49] == 0x2a) { - goto loop1; - } - if (attempt++ > 4) { - goto loop1; + if (result != DriverResult::Success) { + return result; + } + + if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && + nfc_status == NFCStatus::TagLost) { + return DriverResult::ErrorReadingData; + } + + if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) { + std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; + if (output[52] == 0x01) { + memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60); + ntag_buffer_pos += payload_size - 60; + } else { + memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size); } + continue; } - if (result != DriverResult::Success) { - return result; + if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { + LOG_INFO(Input, "Finished reading amiibo"); + return DriverResult::Success; + } + + // Ignore other state reports + if (mcu_report == MCUReport::NFCState) { + continue; } + if (tries++ > timeout_limit) { return DriverResult::Timeout; } @@ -298,7 +288,7 @@ DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) { .crc = {}, }; - std::vector<u8> request_data(sizeof(NFCRequestState)); + std::array<u8, sizeof(NFCRequestState)> request_data{}; memcpy(request_data.data(), &request, sizeof(NFCRequestState)); request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); @@ -315,7 +305,7 @@ DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) { .crc = {}, }; - std::vector<u8> request_data(sizeof(NFCRequestState)); + std::array<u8, sizeof(NFCRequestState)> request_data{}; memcpy(request_data.data(), &request, sizeof(NFCRequestState)); request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); @@ -338,7 +328,7 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); } -DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, std::size_t ntag_pages) { +DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages) { NFCRequestState request{ .sub_command = MCUSubCommand::ReadDeviceMode, .command_argument = NFCReadCommand::Ntag, @@ -357,20 +347,19 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, std::si .crc = {}, }; - std::vector<u8> request_data(sizeof(NFCRequestState)); + std::array<u8, sizeof(NFCRequestState)> request_data{}; memcpy(request_data.data(), &request, sizeof(NFCRequestState)); request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); } -NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(std::size_t pages) const { - if (pages == 0) { +NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { + switch (pages) { + case NFCPages::Block0: return { .block_count = 1, }; - } - - if (pages == 45) { + case NFCPages::Block45: return { .block_count = 1, .blocks = @@ -378,9 +367,7 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(std::size_t pages) const { NFCReadBlock{0x00, 0x2C}, }, }; - } - - if (pages == 135) { + case NFCPages::Block135: return { .block_count = 3, .blocks = @@ -390,9 +377,7 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(std::size_t pages) const { {0x78, 0x86}, }, }; - } - - if (pages == 231) { + case NFCPages::Block231: return { .block_count = 4, .blocks = @@ -403,9 +388,9 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(std::size_t pages) const { {0xb4, 0xe6}, }, }; - } - - return {}; + default: + return {}; + }; } bool NfcProtocol::IsEnabled() const { diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index 5cb0e5a652..e63665aa97 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -51,9 +51,9 @@ private: DriverResult SendStartWaitingRecieveRequest(std::vector<u8>& output); - DriverResult SendReadAmiiboRequest(std::vector<u8>& output, std::size_t ntag_pages); + DriverResult SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages); - NFCReadBlockCommand GetReadBlockCommand(std::size_t pages) const; + NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; bool is_enabled{}; }; diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index 940b20b7f0..7f8e093faf 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -224,9 +224,9 @@ void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) { Joycon::PasivePadButton::StickL, }; - for (std::size_t i = 0; i < left_buttons.size(); ++i) { - const bool button_status = (input.button_input & static_cast<u32>(left_buttons[i])) != 0; - const int button = static_cast<int>(left_buttons[i]); + for (auto left_button : left_buttons) { + const bool button_status = (input.button_input & static_cast<u32>(left_button)) != 0; + const int button = static_cast<int>(left_button); callbacks.on_button_data(button, button_status); } } @@ -241,9 +241,9 @@ void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) { Joycon::PasivePadButton::StickR, }; - for (std::size_t i = 0; i < right_buttons.size(); ++i) { - const bool button_status = (input.button_input & static_cast<u32>(right_buttons[i])) != 0; - const int button = static_cast<int>(right_buttons[i]); + for (auto right_button : right_buttons) { + const bool button_status = (input.button_input & static_cast<u32>(right_button)) != 0; + const int button = static_cast<int>(right_button); callbacks.on_button_data(button, button_status); } } @@ -259,9 +259,9 @@ void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) { Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR, }; - for (std::size_t i = 0; i < pro_buttons.size(); ++i) { - const bool button_status = (input.button_input & static_cast<u32>(pro_buttons[i])) != 0; - const int button = static_cast<int>(pro_buttons[i]); + for (auto pro_button : pro_buttons) { + const bool button_status = (input.button_input & static_cast<u32>(pro_button)) != 0; + const int button = static_cast<int>(pro_button); callbacks.on_button_data(button, button_status); } } diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp index 8adad57dd6..12f81309e1 100644 --- a/src/input_common/helpers/joycon_protocol/ringcon.cpp +++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp @@ -11,8 +11,8 @@ RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle) DriverResult RingConProtocol::EnableRingCon() { LOG_DEBUG(Input, "Enable Ringcon"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; - SetBlocking(); if (result == DriverResult::Success) { result = SetReportMode(ReportMode::STANDARD_FULL_60HZ); @@ -30,14 +30,13 @@ DriverResult RingConProtocol::EnableRingCon() { result = ConfigureMCU(config); } - SetNonBlocking(); return result; } DriverResult RingConProtocol::DisableRingCon() { LOG_DEBUG(Input, "Disable RingCon"); + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; - SetBlocking(); if (result == DriverResult::Success) { result = EnableMCU(false); @@ -45,15 +44,14 @@ DriverResult RingConProtocol::DisableRingCon() { is_enabled = false; - SetNonBlocking(); return result; } DriverResult RingConProtocol::StartRingconPolling() { LOG_DEBUG(Input, "Enable Ringcon"); - bool is_connected = false; + ScopedSetBlocking sb(this); DriverResult result{DriverResult::Success}; - SetBlocking(); + bool is_connected = false; if (result == DriverResult::Success) { result = IsRingConnected(is_connected); @@ -66,13 +64,13 @@ DriverResult RingConProtocol::StartRingconPolling() { is_enabled = true; } - SetNonBlocking(); return result; } DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { LOG_DEBUG(Input, "IsRingConnected"); constexpr std::size_t max_tries = 28; + constexpr u8 ring_controller_id = 0x20; std::vector<u8> output; std::size_t tries = 0; is_connected = false; @@ -88,7 +86,7 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { if (tries++ >= max_tries) { return DriverResult::NoDeviceDetected; } - } while (output[14] != 0x59 || output[16] != 0x20); + } while (output[16] != ring_controller_id); is_connected = true; return DriverResult::Success; @@ -96,30 +94,20 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { DriverResult RingConProtocol::ConfigureRing() { LOG_DEBUG(Input, "ConfigureRing"); - constexpr std::size_t max_tries = 28; - DriverResult result{DriverResult::Success}; - std::vector<u8> output; - std::size_t tries = 0; static constexpr std::array<u8, 37> ring_config{ 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36, 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; - do { - result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config, output); - if (result != DriverResult::Success) { - return result; - } - if (tries++ >= max_tries) { - return DriverResult::NoDeviceDetected; - } - } while (output[14] != 0x5C); + const DriverResult result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config); - static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02}; - result = SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data, output); + if (result != DriverResult::Success) { + return result; + } - return result; + static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02}; + return SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data); } bool RingConProtocol::IsEnabled() const { diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp index fad67a94ba..63b60c9468 100644 --- a/src/input_common/helpers/joycon_protocol/rumble.cpp +++ b/src/input_common/helpers/joycon_protocol/rumble.cpp @@ -14,12 +14,9 @@ RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle) DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { LOG_DEBUG(Input, "Enable Rumble"); + ScopedSetBlocking sb(this); const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; - std::vector<u8> output; - SetBlocking(); - const auto result = SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer, output); - SetNonBlocking(); - return result; + return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer); } DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) { @@ -66,9 +63,9 @@ u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const { } u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const { - /* More information about these values can be found here: - * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md - */ + // More information about these values can be found here: + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md + static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ std::pair<f32, int>{0.0f, 0x0}, {0.01f, 0x2}, @@ -183,9 +180,9 @@ u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const { } u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const { - /* More information about these values can be found here: - * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md - */ + // More information about these values can be found here: + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md + static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ std::pair<f32, int>{0.0f, 0x0040}, {0.01f, 0x8040}, -- cgit v1.2.3-70-g09d2 From fafa92cfb8f78f3a3adaf5bc87f35f495a70ec3f Mon Sep 17 00:00:00 2001 From: german77 <juangerman-13@hotmail.com> Date: Sat, 14 Jan 2023 00:36:03 -0600 Subject: input_common: Fix joycon mappings --- src/input_common/drivers/joycon.cpp | 105 +++++++++++++++++------------------- src/input_common/drivers/joycon.h | 5 +- 2 files changed, 53 insertions(+), 57 deletions(-) (limited to 'src/input_common/drivers/joycon.cpp') diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 1582def13c..7122093c62 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -411,13 +411,25 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifie } PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const { + const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)}; return { - .guid = Common::UUID{Common::InvalidUUID}, + .guid = Common::UUID{guid}, .port = port, .pad = static_cast<std::size_t>(type), }; } +Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const { + const auto identifier = GetIdentifier(port, type); + return { + {"engine", GetEngineName()}, + {"guid", identifier.guid.RawString()}, + {"port", std::to_string(identifier.port)}, + {"pad", std::to_string(identifier.pad)}, + }; +} + std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { std::vector<Common::ParamPackage> devices{}; @@ -428,14 +440,11 @@ std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { if (!device->IsConnected()) { return; } + auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType()); std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()), device->GetDevicePort() + 1); - devices.emplace_back(Common::ParamPackage{ - {"engine", GetEngineName()}, - {"display", std::move(name)}, - {"port", std::to_string(device->GetDevicePort())}, - {"pad", std::to_string(static_cast<std::size_t>(device->GetHandleDeviceType()))}, - }); + param.Set("display", std::move(name)); + devices.emplace_back(param); }; for (const auto& controller : left_joycons) { @@ -453,14 +462,15 @@ std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) { continue; } - constexpr auto type = Joycon::ControllerType::Dual; + auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType()); + const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType()); + const auto type = Joycon::ControllerType::Dual; std::string name = fmt::format("{} {}", JoyconName(type), i + 1); - devices.emplace_back(Common::ParamPackage{ - {"engine", GetEngineName()}, - {"display", std::move(name)}, - {"port", std::to_string(i)}, - {"pad", std::to_string(static_cast<std::size_t>(type))}, - }); + + main_param.Set("display", std::move(name)); + main_param.Set("guid2", second_param.Get("guid", "")); + main_param.Set("pad", std::to_string(static_cast<size_t>(type))); + devices.emplace_back(main_param); } return devices; @@ -496,26 +506,21 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par ButtonMapping mapping{}; for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) { - int pad = params.Get("pad", 0); - if (pad == static_cast<int>(Joycon::ControllerType::Dual)) { - pad = side ? static_cast<int>(Joycon::ControllerType::Right) - : static_cast<int>(Joycon::ControllerType::Left); + const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); + auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0)); + if (pad == Joycon::ControllerType::Dual) { + pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left; } - Common::ParamPackage button_params{}; - button_params.Set("engine", GetEngineName()); - button_params.Set("port", params.Get("port", 0)); - button_params.Set("pad", pad); + Common::ParamPackage button_params = GetParamPackage(port, pad); button_params.Set("button", static_cast<int>(joycon_button)); mapping.insert_or_assign(switch_button, std::move(button_params)); } // Map SL and SR buttons for left joycons if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) { - Common::ParamPackage button_params{}; - button_params.Set("engine", GetEngineName()); - button_params.Set("port", params.Get("port", 0)); - button_params.Set("pad", static_cast<int>(Joycon::ControllerType::Left)); + const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); + Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left); Common::ParamPackage sl_button_params = button_params; Common::ParamPackage sr_button_params = button_params; @@ -527,10 +532,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par // Map SL and SR buttons for right joycons if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) { - Common::ParamPackage button_params{}; - button_params.Set("engine", GetEngineName()); - button_params.Set("port", params.Get("port", 0)); - button_params.Set("pad", static_cast<int>(Joycon::ControllerType::Right)); + const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); + Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right); Common::ParamPackage sl_button_params = button_params; Common::ParamPackage sr_button_params = button_params; @@ -548,25 +551,20 @@ AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& par return {}; } - int pad_left = params.Get("pad", 0); - int pad_right = pad_left; - if (pad_left == static_cast<int>(Joycon::ControllerType::Dual)) { - pad_left = static_cast<int>(Joycon::ControllerType::Left); - pad_right = static_cast<int>(Joycon::ControllerType::Right); + const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); + auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0)); + auto pad_right = pad_left; + if (pad_left == Joycon::ControllerType::Dual) { + pad_left = Joycon::ControllerType::Left; + pad_right = Joycon::ControllerType::Right; } AnalogMapping mapping = {}; - Common::ParamPackage left_analog_params; - left_analog_params.Set("engine", GetEngineName()); - left_analog_params.Set("port", params.Get("port", 0)); - left_analog_params.Set("pad", pad_left); + Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left); left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX)); left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY)); mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); - Common::ParamPackage right_analog_params; - right_analog_params.Set("engine", GetEngineName()); - right_analog_params.Set("port", params.Get("port", 0)); - right_analog_params.Set("pad", pad_right); + Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right); right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX)); right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY)); mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params)); @@ -578,24 +576,19 @@ MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& par return {}; } - int pad_left = params.Get("pad", 0); - int pad_right = pad_left; - if (pad_left == static_cast<int>(Joycon::ControllerType::Dual)) { - pad_left = static_cast<int>(Joycon::ControllerType::Left); - pad_right = static_cast<int>(Joycon::ControllerType::Right); + const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); + auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0)); + auto pad_right = pad_left; + if (pad_left == Joycon::ControllerType::Dual) { + pad_left = Joycon::ControllerType::Left; + pad_right = Joycon::ControllerType::Right; } MotionMapping mapping = {}; - Common::ParamPackage left_motion_params; - left_motion_params.Set("engine", GetEngineName()); - left_motion_params.Set("port", params.Get("port", 0)); - left_motion_params.Set("pad", pad_left); + Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left); left_motion_params.Set("motion", 0); mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params)); - Common::ParamPackage right_Motion_params; - right_Motion_params.Set("engine", GetEngineName()); - right_Motion_params.Set("port", params.Get("port", 0)); - right_Motion_params.Set("pad", pad_right); + Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right); right_Motion_params.Set("motion", 1); mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params)); return mapping; diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index 6d2e2ec786..316d383d88 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -88,9 +88,12 @@ private: /// Returns a JoyconHandle corresponding to a PadIdentifier std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const; - /// Returns a PadIdentifier corresponding to the port number + /// Returns a PadIdentifier corresponding to the port number and joycon type PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const; + /// Returns a ParamPackage corresponding to the port number and joycon type + Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const; + std::string JoyconName(std::size_t port) const; Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; -- cgit v1.2.3-70-g09d2