diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/input_common/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/input_common/drivers/joycon.cpp | 47 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.cpp | 100 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.h | 23 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/poller.cpp | 315 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/poller.h | 77 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/rumble.cpp | 299 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/rumble.h | 33 |
8 files changed, 798 insertions, 100 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index d4307351c..4ab1ccbfb 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -64,6 +64,10 @@ if (ENABLE_SDL2) | |||
| 64 | helpers/joycon_protocol/generic_functions.cpp | 64 | helpers/joycon_protocol/generic_functions.cpp |
| 65 | helpers/joycon_protocol/generic_functions.h | 65 | helpers/joycon_protocol/generic_functions.h |
| 66 | helpers/joycon_protocol/joycon_types.h | 66 | helpers/joycon_protocol/joycon_types.h |
| 67 | helpers/joycon_protocol/poller.cpp | ||
| 68 | helpers/joycon_protocol/poller.h | ||
| 69 | helpers/joycon_protocol/rumble.cpp | ||
| 70 | helpers/joycon_protocol/rumble.h | ||
| 67 | ) | 71 | ) |
| 68 | target_link_libraries(input_common PRIVATE SDL2::SDL2) | 72 | target_link_libraries(input_common PRIVATE SDL2::SDL2) |
| 69 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) | 73 | 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 c6f78c989..dbe730e1a 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) { | |||
| 167 | if (result == Joycon::DriverResult::Success) { | 167 | if (result == Joycon::DriverResult::Success) { |
| 168 | LOG_WARNING(Input, "Initialize device"); | 168 | LOG_WARNING(Input, "Initialize device"); |
| 169 | 169 | ||
| 170 | std::function<void(Joycon::Battery)> on_battery_data; | ||
| 171 | std::function<void(Joycon::Color)> on_button_data; | ||
| 172 | std::function<void(int, f32)> on_stick_data; | ||
| 173 | std::function<void(int, std::array<u8, 6>)> on_motion_data; | ||
| 174 | std::function<void(s16)> on_ring_data; | ||
| 175 | std::function<void(const std::vector<u8>&)> on_amiibo_data; | ||
| 176 | |||
| 177 | const std::size_t port = handle->GetDevicePort(); | 170 | const std::size_t port = handle->GetDevicePort(); |
| 178 | handle->on_battery_data = { | 171 | const Joycon::JoyconCallbacks callbacks{ |
| 179 | [this, port, type](Joycon::Battery value) { OnBatteryUpdate(port, type, value); }}; | 172 | .on_battery_data = {[this, port, type](Joycon::Battery value) { |
| 180 | handle->on_color_data = { | 173 | OnBatteryUpdate(port, type, value); |
| 181 | [this, port, type](Joycon::Color value) { OnColorUpdate(port, type, value); }}; | 174 | }}, |
| 182 | handle->on_button_data = { | 175 | .on_color_data = {[this, port, type](Joycon::Color value) { |
| 183 | [this, port, type](int id, bool value) { OnButtonUpdate(port, type, id, value); }}; | 176 | OnColorUpdate(port, type, value); |
| 184 | handle->on_stick_data = { | 177 | }}, |
| 185 | [this, port, type](int id, f32 value) { OnStickUpdate(port, type, id, value); }}; | 178 | .on_button_data = {[this, port, type](int id, bool value) { |
| 186 | handle->on_motion_data = {[this, port, type](int id, Joycon::MotionData value) { | 179 | OnButtonUpdate(port, type, id, value); |
| 187 | OnMotionUpdate(port, type, id, value); | 180 | }}, |
| 188 | }}; | 181 | .on_stick_data = {[this, port, type](int id, f32 value) { |
| 189 | handle->on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}; | 182 | OnStickUpdate(port, type, id, value); |
| 190 | handle->on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) { | 183 | }}, |
| 191 | OnAmiiboUpdate(port, amiibo_data); | 184 | .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) { |
| 192 | }}; | 185 | OnMotionUpdate(port, type, id, value); |
| 186 | }}, | ||
| 187 | .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, | ||
| 188 | .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) { | ||
| 189 | OnAmiiboUpdate(port, amiibo_data); | ||
| 190 | }}, | ||
| 191 | }; | ||
| 192 | |||
| 193 | handle->InitializeDevice(); | 193 | handle->InitializeDevice(); |
| 194 | handle->SetCallbacks(callbacks); | ||
| 194 | } | 195 | } |
| 195 | } | 196 | } |
| 196 | 197 | ||
| @@ -235,7 +236,7 @@ Common::Input::VibrationError Joycons::SetVibration( | |||
| 235 | .low_amplitude = vibration.low_amplitude, | 236 | .low_amplitude = vibration.low_amplitude, |
| 236 | .low_frequency = vibration.low_frequency, | 237 | .low_frequency = vibration.low_frequency, |
| 237 | .high_amplitude = vibration.high_amplitude, | 238 | .high_amplitude = vibration.high_amplitude, |
| 238 | .high_frequency = vibration.high_amplitude, | 239 | .high_frequency = vibration.high_frequency, |
| 239 | }; | 240 | }; |
| 240 | auto handle = GetHandle(identifier); | 241 | auto handle = GetHandle(identifier); |
| 241 | if (handle == nullptr) { | 242 | if (handle == nullptr) { |
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index ac11be1c1..5d0aeabf5 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp | |||
| @@ -66,6 +66,7 @@ DriverResult JoyconDriver::InitializeDevice() { | |||
| 66 | // Initialize HW Protocols | 66 | // Initialize HW Protocols |
| 67 | calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle); | 67 | calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle); |
| 68 | generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); | 68 | generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); |
| 69 | rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); | ||
| 69 | 70 | ||
| 70 | // Get fixed joycon info | 71 | // Get fixed joycon info |
| 71 | generic_protocol->GetVersionNumber(version); | 72 | generic_protocol->GetVersionNumber(version); |
| @@ -90,6 +91,10 @@ DriverResult JoyconDriver::InitializeDevice() { | |||
| 90 | // Apply HW configuration | 91 | // Apply HW configuration |
| 91 | SetPollingMode(); | 92 | SetPollingMode(); |
| 92 | 93 | ||
| 94 | // Initialize joycon poller | ||
| 95 | joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, | ||
| 96 | right_stick_calibration, motion_calibration); | ||
| 97 | |||
| 93 | // Start pooling for data | 98 | // Start pooling for data |
| 94 | is_connected = true; | 99 | is_connected = true; |
| 95 | if (!input_thread_running) { | 100 | if (!input_thread_running) { |
| @@ -142,15 +147,40 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { | |||
| 142 | void JoyconDriver::OnNewData(std::span<u8> buffer) { | 147 | void JoyconDriver::OnNewData(std::span<u8> buffer) { |
| 143 | const auto report_mode = static_cast<InputReport>(buffer[0]); | 148 | const auto report_mode = static_cast<InputReport>(buffer[0]); |
| 144 | 149 | ||
| 150 | // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion | ||
| 151 | // experience | ||
| 152 | switch (report_mode) { | ||
| 153 | case InputReport::STANDARD_FULL_60HZ: | ||
| 154 | case InputReport::NFC_IR_MODE_60HZ: | ||
| 155 | case InputReport::SIMPLE_HID_MODE: { | ||
| 156 | const auto now = std::chrono::steady_clock::now(); | ||
| 157 | const auto new_delta_time = static_cast<u64>( | ||
| 158 | std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); | ||
| 159 | delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10; | ||
| 160 | last_update = now; | ||
| 161 | joycon_poller->UpdateColor(color); | ||
| 162 | break; | ||
| 163 | } | ||
| 164 | default: | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | |||
| 168 | const MotionStatus motion_status{ | ||
| 169 | .is_enabled = motion_enabled, | ||
| 170 | .delta_time = delta_time, | ||
| 171 | .gyro_sensitivity = gyro_sensitivity, | ||
| 172 | .accelerometer_sensitivity = accelerometer_sensitivity, | ||
| 173 | }; | ||
| 174 | |||
| 145 | switch (report_mode) { | 175 | switch (report_mode) { |
| 146 | case InputReport::STANDARD_FULL_60HZ: | 176 | case InputReport::STANDARD_FULL_60HZ: |
| 147 | ReadActiveMode(buffer); | 177 | joycon_poller->ReadActiveMode(buffer, motion_status); |
| 148 | break; | 178 | break; |
| 149 | case InputReport::NFC_IR_MODE_60HZ: | 179 | case InputReport::NFC_IR_MODE_60HZ: |
| 150 | ReadNfcIRMode(buffer); | 180 | joycon_poller->ReadNfcIRMode(buffer, motion_status); |
| 151 | break; | 181 | break; |
| 152 | case InputReport::SIMPLE_HID_MODE: | 182 | case InputReport::SIMPLE_HID_MODE: |
| 153 | ReadPassiveMode(buffer); | 183 | joycon_poller->ReadPassiveMode(buffer); |
| 154 | break; | 184 | break; |
| 155 | case InputReport::SUBCMD_REPLY: | 185 | case InputReport::SUBCMD_REPLY: |
| 156 | LOG_DEBUG(Input, "Unhandled command reply"); | 186 | LOG_DEBUG(Input, "Unhandled command reply"); |
| @@ -164,6 +194,8 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { | |||
| 164 | void JoyconDriver::SetPollingMode() { | 194 | void JoyconDriver::SetPollingMode() { |
| 165 | disable_input_thread = true; | 195 | disable_input_thread = true; |
| 166 | 196 | ||
| 197 | rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); | ||
| 198 | |||
| 167 | if (motion_enabled && supported_features.motion) { | 199 | if (motion_enabled && supported_features.motion) { |
| 168 | generic_protocol->EnableImu(true); | 200 | generic_protocol->EnableImu(true); |
| 169 | generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance, | 201 | generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance, |
| @@ -209,62 +241,6 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() { | |||
| 209 | return features; | 241 | return features; |
| 210 | } | 242 | } |
| 211 | 243 | ||
| 212 | void JoyconDriver::ReadActiveMode(std::span<u8> buffer) { | ||
| 213 | InputReportActive data{}; | ||
| 214 | memcpy(&data, buffer.data(), sizeof(InputReportActive)); | ||
| 215 | |||
| 216 | // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion | ||
| 217 | // experience | ||
| 218 | const auto now = std::chrono::steady_clock::now(); | ||
| 219 | const auto new_delta_time = | ||
| 220 | std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count(); | ||
| 221 | delta_time = static_cast<u64>((delta_time * 0.8f) + (new_delta_time * 0.2)); | ||
| 222 | last_update = now; | ||
| 223 | |||
| 224 | switch (device_type) { | ||
| 225 | case Joycon::ControllerType::Left: | ||
| 226 | break; | ||
| 227 | case Joycon::ControllerType::Right: | ||
| 228 | break; | ||
| 229 | case Joycon::ControllerType::Pro: | ||
| 230 | break; | ||
| 231 | case Joycon::ControllerType::Grip: | ||
| 232 | case Joycon::ControllerType::Dual: | ||
| 233 | case Joycon::ControllerType::None: | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | |||
| 237 | on_battery_data(data.battery_status); | ||
| 238 | on_color_data(color); | ||
| 239 | } | ||
| 240 | |||
| 241 | void JoyconDriver::ReadPassiveMode(std::span<u8> buffer) { | ||
| 242 | InputReportPassive data{}; | ||
| 243 | memcpy(&data, buffer.data(), sizeof(InputReportPassive)); | ||
| 244 | |||
| 245 | switch (device_type) { | ||
| 246 | case Joycon::ControllerType::Left: | ||
| 247 | break; | ||
| 248 | case Joycon::ControllerType::Right: | ||
| 249 | break; | ||
| 250 | case Joycon::ControllerType::Pro: | ||
| 251 | break; | ||
| 252 | case Joycon::ControllerType::Grip: | ||
| 253 | case Joycon::ControllerType::Dual: | ||
| 254 | case Joycon::ControllerType::None: | ||
| 255 | break; | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | void JoyconDriver::ReadNfcIRMode(std::span<u8> buffer) { | ||
| 260 | // This mode is compatible with the active mode | ||
| 261 | ReadActiveMode(buffer); | ||
| 262 | |||
| 263 | if (!nfc_enabled) { | ||
| 264 | return; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | bool JoyconDriver::IsInputThreadValid() const { | 244 | bool JoyconDriver::IsInputThreadValid() const { |
| 269 | if (!is_connected) { | 245 | if (!is_connected) { |
| 270 | return false; | 246 | return false; |
| @@ -302,7 +278,7 @@ DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { | |||
| 302 | if (disable_input_thread) { | 278 | if (disable_input_thread) { |
| 303 | return DriverResult::HandleInUse; | 279 | return DriverResult::HandleInUse; |
| 304 | } | 280 | } |
| 305 | return DriverResult::NotSupported; | 281 | return rumble_protocol->SendVibration(vibration); |
| 306 | } | 282 | } |
| 307 | 283 | ||
| 308 | DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { | 284 | DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { |
| @@ -398,6 +374,10 @@ SerialNumber JoyconDriver::GetHandleSerialNumber() const { | |||
| 398 | return handle_serial_number; | 374 | return handle_serial_number; |
| 399 | } | 375 | } |
| 400 | 376 | ||
| 377 | void JoyconDriver::SetCallbacks(const Joycon::JoyconCallbacks& callbacks) { | ||
| 378 | joycon_poller->SetCallbacks(callbacks); | ||
| 379 | } | ||
| 380 | |||
| 401 | Joycon::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, | 381 | Joycon::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, |
| 402 | ControllerType& controller_type) { | 382 | ControllerType& controller_type) { |
| 403 | std::array<std::pair<u32, Joycon::ControllerType>, 4> supported_devices{ | 383 | 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 275c97b91..48ba859f4 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h | |||
| @@ -11,6 +11,8 @@ | |||
| 11 | #include "input_common/helpers/joycon_protocol/calibration.h" | 11 | #include "input_common/helpers/joycon_protocol/calibration.h" |
| 12 | #include "input_common/helpers/joycon_protocol/generic_functions.h" | 12 | #include "input_common/helpers/joycon_protocol/generic_functions.h" |
| 13 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | 13 | #include "input_common/helpers/joycon_protocol/joycon_types.h" |
| 14 | #include "input_common/helpers/joycon_protocol/poller.h" | ||
| 15 | #include "input_common/helpers/joycon_protocol/rumble.h" | ||
| 14 | 16 | ||
| 15 | namespace InputCommon::Joycon { | 17 | namespace InputCommon::Joycon { |
| 16 | 18 | ||
| @@ -42,6 +44,8 @@ public: | |||
| 42 | DriverResult SetNfcMode(); | 44 | DriverResult SetNfcMode(); |
| 43 | DriverResult SetRingConMode(); | 45 | DriverResult SetRingConMode(); |
| 44 | 46 | ||
| 47 | void SetCallbacks(const Joycon::JoyconCallbacks& callbacks); | ||
| 48 | |||
| 45 | // Returns device type from hidapi handle | 49 | // Returns device type from hidapi handle |
| 46 | static Joycon::DriverResult GetDeviceType(SDL_hid_device_info* device_info, | 50 | static Joycon::DriverResult GetDeviceType(SDL_hid_device_info* device_info, |
| 47 | Joycon::ControllerType& controller_type); | 51 | Joycon::ControllerType& controller_type); |
| @@ -50,14 +54,6 @@ public: | |||
| 50 | static Joycon::DriverResult GetSerialNumber(SDL_hid_device_info* device_info, | 54 | static Joycon::DriverResult GetSerialNumber(SDL_hid_device_info* device_info, |
| 51 | Joycon::SerialNumber& serial_number); | 55 | Joycon::SerialNumber& serial_number); |
| 52 | 56 | ||
| 53 | std::function<void(Battery)> on_battery_data; | ||
| 54 | std::function<void(Color)> on_color_data; | ||
| 55 | std::function<void(int, bool)> on_button_data; | ||
| 56 | std::function<void(int, f32)> on_stick_data; | ||
| 57 | std::function<void(int, MotionData)> on_motion_data; | ||
| 58 | std::function<void(f32)> on_ring_data; | ||
| 59 | std::function<void(const std::vector<u8>&)> on_amiibo_data; | ||
| 60 | |||
| 61 | private: | 57 | private: |
| 62 | struct SupportedFeatures { | 58 | struct SupportedFeatures { |
| 63 | bool passive{}; | 59 | bool passive{}; |
| @@ -86,18 +82,11 @@ private: | |||
| 86 | /// Returns a list of supported features that can be enabled on this device | 82 | /// Returns a list of supported features that can be enabled on this device |
| 87 | SupportedFeatures GetSupportedFeatures(); | 83 | SupportedFeatures GetSupportedFeatures(); |
| 88 | 84 | ||
| 89 | /// Handles data from passive packages | ||
| 90 | void ReadPassiveMode(std::span<u8> buffer); | ||
| 91 | |||
| 92 | /// Handles data from active packages | ||
| 93 | void ReadActiveMode(std::span<u8> buffer); | ||
| 94 | |||
| 95 | /// Handles data from nfc or ir packages | ||
| 96 | void ReadNfcIRMode(std::span<u8> buffer); | ||
| 97 | |||
| 98 | // Protocol Features | 85 | // Protocol Features |
| 99 | std::unique_ptr<CalibrationProtocol> calibration_protocol = nullptr; | 86 | std::unique_ptr<CalibrationProtocol> calibration_protocol = nullptr; |
| 100 | std::unique_ptr<GenericProtocol> generic_protocol = nullptr; | 87 | std::unique_ptr<GenericProtocol> generic_protocol = nullptr; |
| 88 | std::unique_ptr<JoyconPoller> joycon_poller = nullptr; | ||
| 89 | std::unique_ptr<RumbleProtocol> rumble_protocol = nullptr; | ||
| 101 | 90 | ||
| 102 | // Connection status | 91 | // Connection status |
| 103 | bool is_connected{}; | 92 | 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 000000000..341479c0c --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/poller.cpp | |||
| @@ -0,0 +1,315 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "input_common/helpers/joycon_protocol/poller.h" | ||
| 6 | |||
| 7 | namespace InputCommon::Joycon { | ||
| 8 | |||
| 9 | JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_, | ||
| 10 | JoyStickCalibration right_stick_calibration_, | ||
| 11 | MotionCalibration motion_calibration_) | ||
| 12 | : device_type{device_type_}, left_stick_calibration{left_stick_calibration_}, | ||
| 13 | right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {} | ||
| 14 | |||
| 15 | void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) { | ||
| 16 | callbacks = std::move(callbacks_); | ||
| 17 | } | ||
| 18 | |||
| 19 | void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status) { | ||
| 20 | InputReportActive data{}; | ||
| 21 | memcpy(&data, buffer.data(), sizeof(InputReportActive)); | ||
| 22 | |||
| 23 | switch (device_type) { | ||
| 24 | case Joycon::ControllerType::Left: | ||
| 25 | UpdateActiveLeftPadInput(data, motion_status); | ||
| 26 | break; | ||
| 27 | case Joycon::ControllerType::Right: | ||
| 28 | UpdateActiveRightPadInput(data, motion_status); | ||
| 29 | break; | ||
| 30 | case Joycon::ControllerType::Pro: | ||
| 31 | UpdateActiveProPadInput(data, motion_status); | ||
| 32 | break; | ||
| 33 | case Joycon::ControllerType::Grip: | ||
| 34 | case Joycon::ControllerType::Dual: | ||
| 35 | case Joycon::ControllerType::None: | ||
| 36 | break; | ||
| 37 | } | ||
| 38 | |||
| 39 | callbacks.on_battery_data(data.battery_status); | ||
| 40 | } | ||
| 41 | |||
| 42 | void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) { | ||
| 43 | InputReportPassive data{}; | ||
| 44 | memcpy(&data, buffer.data(), sizeof(InputReportPassive)); | ||
| 45 | |||
| 46 | switch (device_type) { | ||
| 47 | case Joycon::ControllerType::Left: | ||
| 48 | UpdatePasiveLeftPadInput(data); | ||
| 49 | break; | ||
| 50 | case Joycon::ControllerType::Right: | ||
| 51 | UpdatePasiveRightPadInput(data); | ||
| 52 | break; | ||
| 53 | case Joycon::ControllerType::Pro: | ||
| 54 | UpdatePasiveProPadInput(data); | ||
| 55 | break; | ||
| 56 | case Joycon::ControllerType::Grip: | ||
| 57 | case Joycon::ControllerType::Dual: | ||
| 58 | case Joycon::ControllerType::None: | ||
| 59 | break; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) { | ||
| 64 | // This mode is compatible with the active mode | ||
| 65 | ReadActiveMode(buffer, motion_status); | ||
| 66 | } | ||
| 67 | |||
| 68 | void JoyconPoller::UpdateColor(const Color& color) { | ||
| 69 | callbacks.on_color_data(color); | ||
| 70 | } | ||
| 71 | |||
| 72 | void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input, | ||
| 73 | const MotionStatus& motion_status) { | ||
| 74 | static constexpr std::array<Joycon::PadButton, 11> left_buttons{ | ||
| 75 | Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right, | ||
| 76 | Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR, | ||
| 77 | Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus, | ||
| 78 | Joycon::PadButton::Capture, Joycon::PadButton::StickL, | ||
| 79 | }; | ||
| 80 | |||
| 81 | const u32 raw_button = | ||
| 82 | static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16)); | ||
| 83 | for (std::size_t i = 0; i < left_buttons.size(); ++i) { | ||
| 84 | const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0; | ||
| 85 | const int button = static_cast<int>(left_buttons[i]); | ||
| 86 | callbacks.on_button_data(button, button_status); | ||
| 87 | } | ||
| 88 | |||
| 89 | const u16 raw_left_axis_x = | ||
| 90 | static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8)); | ||
| 91 | const u16 raw_left_axis_y = | ||
| 92 | static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4)); | ||
| 93 | const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x); | ||
| 94 | const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y); | ||
| 95 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x); | ||
| 96 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y); | ||
| 97 | |||
| 98 | if (motion_status.is_enabled) { | ||
| 99 | auto left_motion = GetMotionInput(input, motion_status); | ||
| 100 | // Rotate motion axis to the correct direction | ||
| 101 | left_motion.accel_y = -left_motion.accel_y; | ||
| 102 | left_motion.accel_z = -left_motion.accel_z; | ||
| 103 | left_motion.gyro_x = -left_motion.gyro_x; | ||
| 104 | callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input, | ||
| 109 | const MotionStatus& motion_status) { | ||
| 110 | static constexpr std::array<Joycon::PadButton, 11> right_buttons{ | ||
| 111 | Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B, | ||
| 112 | Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR, | ||
| 113 | Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus, | ||
| 114 | Joycon::PadButton::Home, Joycon::PadButton::StickR, | ||
| 115 | }; | ||
| 116 | |||
| 117 | const u32 raw_button = | ||
| 118 | static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16)); | ||
| 119 | for (std::size_t i = 0; i < right_buttons.size(); ++i) { | ||
| 120 | const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0; | ||
| 121 | const int button = static_cast<int>(right_buttons[i]); | ||
| 122 | callbacks.on_button_data(button, button_status); | ||
| 123 | } | ||
| 124 | |||
| 125 | const u16 raw_right_axis_x = | ||
| 126 | static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8)); | ||
| 127 | const u16 raw_right_axis_y = | ||
| 128 | static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4)); | ||
| 129 | const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x); | ||
| 130 | const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y); | ||
| 131 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x); | ||
| 132 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y); | ||
| 133 | |||
| 134 | if (motion_status.is_enabled) { | ||
| 135 | auto right_motion = GetMotionInput(input, motion_status); | ||
| 136 | // Rotate motion axis to the correct direction | ||
| 137 | right_motion.accel_x = -right_motion.accel_x; | ||
| 138 | right_motion.accel_y = -right_motion.accel_y; | ||
| 139 | right_motion.gyro_z = -right_motion.gyro_z; | ||
| 140 | callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input, | ||
| 145 | const MotionStatus& motion_status) { | ||
| 146 | static constexpr std::array<Joycon::PadButton, 18> pro_buttons{ | ||
| 147 | Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right, | ||
| 148 | Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL, | ||
| 149 | Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y, | ||
| 150 | Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A, | ||
| 151 | Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus, | ||
| 152 | Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR, | ||
| 153 | }; | ||
| 154 | |||
| 155 | const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) | | ||
| 156 | (input.button_input[1] << 16)); | ||
| 157 | for (std::size_t i = 0; i < pro_buttons.size(); ++i) { | ||
| 158 | const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0; | ||
| 159 | const int button = static_cast<int>(pro_buttons[i]); | ||
| 160 | callbacks.on_button_data(button, button_status); | ||
| 161 | } | ||
| 162 | |||
| 163 | const u16 raw_left_axis_x = | ||
| 164 | static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8)); | ||
| 165 | const u16 raw_left_axis_y = | ||
| 166 | static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4)); | ||
| 167 | const u16 raw_right_axis_x = | ||
| 168 | static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8)); | ||
| 169 | const u16 raw_right_axis_y = | ||
| 170 | static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4)); | ||
| 171 | |||
| 172 | const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x); | ||
| 173 | const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y); | ||
| 174 | const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x); | ||
| 175 | const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y); | ||
| 176 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x); | ||
| 177 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y); | ||
| 178 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x); | ||
| 179 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y); | ||
| 180 | |||
| 181 | if (motion_status.is_enabled) { | ||
| 182 | auto pro_motion = GetMotionInput(input, motion_status); | ||
| 183 | pro_motion.gyro_x = -pro_motion.gyro_x; | ||
| 184 | pro_motion.accel_y = -pro_motion.accel_y; | ||
| 185 | pro_motion.accel_z = -pro_motion.accel_z; | ||
| 186 | callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion); | ||
| 187 | callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) { | ||
| 192 | static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{ | ||
| 193 | Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, | ||
| 194 | Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, | ||
| 195 | Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, | ||
| 196 | Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, | ||
| 197 | Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture, | ||
| 198 | Joycon::PasivePadButton::StickL, | ||
| 199 | }; | ||
| 200 | |||
| 201 | for (std::size_t i = 0; i < left_buttons.size(); ++i) { | ||
| 202 | const bool button_status = (input.button_input & static_cast<u32>(left_buttons[i])) != 0; | ||
| 203 | const int button = static_cast<int>(left_buttons[i]); | ||
| 204 | callbacks.on_button_data(button, button_status); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) { | ||
| 209 | static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{ | ||
| 210 | Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, | ||
| 211 | Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, | ||
| 212 | Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, | ||
| 213 | Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, | ||
| 214 | Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home, | ||
| 215 | Joycon::PasivePadButton::StickR, | ||
| 216 | }; | ||
| 217 | |||
| 218 | for (std::size_t i = 0; i < right_buttons.size(); ++i) { | ||
| 219 | const bool button_status = (input.button_input & static_cast<u32>(right_buttons[i])) != 0; | ||
| 220 | const int button = static_cast<int>(right_buttons[i]); | ||
| 221 | callbacks.on_button_data(button, button_status); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) { | ||
| 226 | static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{ | ||
| 227 | Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, | ||
| 228 | Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, | ||
| 229 | Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, | ||
| 230 | Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, | ||
| 231 | Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus, | ||
| 232 | Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home, | ||
| 233 | Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR, | ||
| 234 | }; | ||
| 235 | |||
| 236 | for (std::size_t i = 0; i < pro_buttons.size(); ++i) { | ||
| 237 | const bool button_status = (input.button_input & static_cast<u32>(pro_buttons[i])) != 0; | ||
| 238 | const int button = static_cast<int>(pro_buttons[i]); | ||
| 239 | callbacks.on_button_data(button, button_status); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const { | ||
| 244 | const f32 value = static_cast<f32>(raw_value - calibration.center); | ||
| 245 | if (value > 0.0f) { | ||
| 246 | return value / calibration.max; | ||
| 247 | } | ||
| 248 | return value / calibration.min; | ||
| 249 | } | ||
| 250 | |||
| 251 | f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal, | ||
| 252 | AccelerometerSensitivity sensitivity) const { | ||
| 253 | const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4; | ||
| 254 | switch (sensitivity) { | ||
| 255 | case Joycon::AccelerometerSensitivity::G2: | ||
| 256 | return value / 4.0f; | ||
| 257 | case Joycon::AccelerometerSensitivity::G4: | ||
| 258 | return value / 2.0f; | ||
| 259 | case Joycon::AccelerometerSensitivity::G8: | ||
| 260 | return value; | ||
| 261 | case Joycon::AccelerometerSensitivity::G16: | ||
| 262 | return value * 2.0f; | ||
| 263 | } | ||
| 264 | return value; | ||
| 265 | } | ||
| 266 | |||
| 267 | f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal, | ||
| 268 | GyroSensitivity sensitivity) const { | ||
| 269 | const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f; | ||
| 270 | switch (sensitivity) { | ||
| 271 | case Joycon::GyroSensitivity::DPS250: | ||
| 272 | return value / 8.0f; | ||
| 273 | case Joycon::GyroSensitivity::DPS500: | ||
| 274 | return value / 4.0f; | ||
| 275 | case Joycon::GyroSensitivity::DPS1000: | ||
| 276 | return value / 2.0f; | ||
| 277 | case Joycon::GyroSensitivity::DPS2000: | ||
| 278 | return value; | ||
| 279 | } | ||
| 280 | return value; | ||
| 281 | } | ||
| 282 | |||
| 283 | s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis, | ||
| 284 | const InputReportActive& input) const { | ||
| 285 | return input.motion_input[(sensor * 3) + axis]; | ||
| 286 | } | ||
| 287 | |||
| 288 | MotionData JoyconPoller::GetMotionInput(const InputReportActive& input, | ||
| 289 | const MotionStatus& motion_status) const { | ||
| 290 | MotionData motion{}; | ||
| 291 | const auto& accel_cal = motion_calibration.accelerometer; | ||
| 292 | const auto& gyro_cal = motion_calibration.gyro; | ||
| 293 | const s16 raw_accel_x = input.motion_input[1]; | ||
| 294 | const s16 raw_accel_y = input.motion_input[0]; | ||
| 295 | const s16 raw_accel_z = input.motion_input[2]; | ||
| 296 | const s16 raw_gyro_x = input.motion_input[4]; | ||
| 297 | const s16 raw_gyro_y = input.motion_input[3]; | ||
| 298 | const s16 raw_gyro_z = input.motion_input[5]; | ||
| 299 | |||
| 300 | motion.delta_timestamp = motion_status.delta_time; | ||
| 301 | motion.accel_x = | ||
| 302 | GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity); | ||
| 303 | motion.accel_y = | ||
| 304 | GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity); | ||
| 305 | motion.accel_z = | ||
| 306 | GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity); | ||
| 307 | motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity); | ||
| 308 | motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity); | ||
| 309 | motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity); | ||
| 310 | |||
| 311 | // TODO(German77): Return all three samples data | ||
| 312 | return motion; | ||
| 313 | } | ||
| 314 | |||
| 315 | } // 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 000000000..fff681d0a --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/poller.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <functional> | ||
| 12 | #include <span> | ||
| 13 | |||
| 14 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | |||
| 18 | // Handles input packages and triggers the corresponding input events | ||
| 19 | class JoyconPoller { | ||
| 20 | public: | ||
| 21 | JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_, | ||
| 22 | JoyStickCalibration right_stick_calibration_, | ||
| 23 | MotionCalibration motion_calibration_); | ||
| 24 | |||
| 25 | void SetCallbacks(const Joycon::JoyconCallbacks& callbacks_); | ||
| 26 | |||
| 27 | /// Handles data from passive packages | ||
| 28 | void ReadPassiveMode(std::span<u8> buffer); | ||
| 29 | |||
| 30 | /// Handles data from active packages | ||
| 31 | void ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status); | ||
| 32 | |||
| 33 | /// Handles data from nfc or ir packages | ||
| 34 | void ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status); | ||
| 35 | |||
| 36 | void UpdateColor(const Color& color); | ||
| 37 | |||
| 38 | private: | ||
| 39 | void UpdateActiveLeftPadInput(const InputReportActive& input, | ||
| 40 | const MotionStatus& motion_status); | ||
| 41 | void UpdateActiveRightPadInput(const InputReportActive& input, | ||
| 42 | const MotionStatus& motion_status); | ||
| 43 | void UpdateActiveProPadInput(const InputReportActive& input, const MotionStatus& motion_status); | ||
| 44 | |||
| 45 | void UpdatePasiveLeftPadInput(const InputReportPassive& buffer); | ||
| 46 | void UpdatePasiveRightPadInput(const InputReportPassive& buffer); | ||
| 47 | void UpdatePasiveProPadInput(const InputReportPassive& buffer); | ||
| 48 | |||
| 49 | /// Returns a calibrated joystick axis from raw axis data | ||
| 50 | f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const; | ||
| 51 | |||
| 52 | /// Returns a calibrated accelerometer axis from raw motion data | ||
| 53 | f32 GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal, | ||
| 54 | AccelerometerSensitivity sensitivity) const; | ||
| 55 | |||
| 56 | /// Returns a calibrated gyro axis from raw motion data | ||
| 57 | f32 GetGyroValue(s16 raw_value, const MotionSensorCalibration& cal, | ||
| 58 | GyroSensitivity sensitivity) const; | ||
| 59 | |||
| 60 | /// Returns a raw motion value from a buffer | ||
| 61 | s16 GetRawIMUValues(size_t sensor, size_t axis, const InputReportActive& input) const; | ||
| 62 | |||
| 63 | /// Returns motion data from a buffer | ||
| 64 | MotionData GetMotionInput(const InputReportActive& input, | ||
| 65 | const MotionStatus& motion_status) const; | ||
| 66 | |||
| 67 | ControllerType device_type{}; | ||
| 68 | |||
| 69 | // Device calibration | ||
| 70 | JoyStickCalibration left_stick_calibration{}; | ||
| 71 | JoyStickCalibration right_stick_calibration{}; | ||
| 72 | MotionCalibration motion_calibration{}; | ||
| 73 | |||
| 74 | Joycon::JoyconCallbacks callbacks{}; | ||
| 75 | }; | ||
| 76 | |||
| 77 | } // 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 000000000..17ee38863 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/rumble.cpp | |||
| @@ -0,0 +1,299 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "input_common/helpers/joycon_protocol/rumble.h" | ||
| 6 | |||
| 7 | namespace InputCommon::Joycon { | ||
| 8 | |||
| 9 | RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 10 | : JoyconCommonProtocol(handle) {} | ||
| 11 | |||
| 12 | DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { | ||
| 13 | LOG_DEBUG(Input, "Enable Rumble"); | ||
| 14 | const std::vector<u8> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; | ||
| 15 | std::vector<u8> output; | ||
| 16 | SetBlocking(); | ||
| 17 | const auto result = SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer, output); | ||
| 18 | SetNonBlocking(); | ||
| 19 | return result; | ||
| 20 | } | ||
| 21 | |||
| 22 | DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) { | ||
| 23 | std::vector<u8> buffer(sizeof(DefaultVibrationBuffer)); | ||
| 24 | |||
| 25 | if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) { | ||
| 26 | return SendVibrationReport(DefaultVibrationBuffer); | ||
| 27 | } | ||
| 28 | |||
| 29 | // Protect joycons from damage from strong vibrations | ||
| 30 | const f32 clamp_amplitude = | ||
| 31 | 1.0f / std::max(1.0f, vibration.high_amplitude + vibration.low_amplitude); | ||
| 32 | |||
| 33 | const u16 encoded_high_frequency = EncodeHighFrequency(vibration.high_frequency); | ||
| 34 | const u8 encoded_high_amplitude = | ||
| 35 | EncodeHighAmplitude(vibration.high_amplitude * clamp_amplitude); | ||
| 36 | const u8 encoded_low_frequency = EncodeLowFrequency(vibration.low_frequency); | ||
| 37 | const u16 encoded_low_amplitude = EncodeLowAmplitude(vibration.low_amplitude * clamp_amplitude); | ||
| 38 | |||
| 39 | buffer[0] = static_cast<u8>(encoded_high_frequency & 0xFF); | ||
| 40 | buffer[1] = static_cast<u8>(encoded_high_amplitude | ((encoded_high_frequency >> 8) & 0x01)); | ||
| 41 | buffer[2] = static_cast<u8>(encoded_low_frequency | ((encoded_low_amplitude >> 8) & 0x80)); | ||
| 42 | buffer[3] = static_cast<u8>(encoded_low_amplitude & 0xFF); | ||
| 43 | |||
| 44 | // Duplicate rumble for now | ||
| 45 | buffer[4] = buffer[0]; | ||
| 46 | buffer[5] = buffer[1]; | ||
| 47 | buffer[6] = buffer[2]; | ||
| 48 | buffer[7] = buffer[3]; | ||
| 49 | |||
| 50 | return SendVibrationReport(buffer); | ||
| 51 | } | ||
| 52 | |||
| 53 | u16 RumbleProtocol::EncodeHighFrequency(f32 frequency) const { | ||
| 54 | const u8 new_frequency = | ||
| 55 | static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f)); | ||
| 56 | return static_cast<u16>((new_frequency - 0x60) * 4); | ||
| 57 | } | ||
| 58 | |||
| 59 | u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const { | ||
| 60 | const u8 new_frequency = | ||
| 61 | static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f)); | ||
| 62 | return static_cast<u8>(new_frequency - 0x40); | ||
| 63 | } | ||
| 64 | |||
| 65 | u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const { | ||
| 66 | /* More information about these values can be found here: | ||
| 67 | * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md | ||
| 68 | */ | ||
| 69 | constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ | ||
| 70 | std::pair<f32, int>{0.0f, 0x0}, | ||
| 71 | {0.01f, 0x2}, | ||
| 72 | {0.012f, 0x4}, | ||
| 73 | {0.014f, 0x6}, | ||
| 74 | {0.017f, 0x8}, | ||
| 75 | {0.02f, 0x0a}, | ||
| 76 | {0.024f, 0x0c}, | ||
| 77 | {0.028f, 0x0e}, | ||
| 78 | {0.033f, 0x10}, | ||
| 79 | {0.04f, 0x12}, | ||
| 80 | {0.047f, 0x14}, | ||
| 81 | {0.056f, 0x16}, | ||
| 82 | {0.067f, 0x18}, | ||
| 83 | {0.08f, 0x1a}, | ||
| 84 | {0.095f, 0x1c}, | ||
| 85 | {0.112f, 0x1e}, | ||
| 86 | {0.117f, 0x20}, | ||
| 87 | {0.123f, 0x22}, | ||
| 88 | {0.128f, 0x24}, | ||
| 89 | {0.134f, 0x26}, | ||
| 90 | {0.14f, 0x28}, | ||
| 91 | {0.146f, 0x2a}, | ||
| 92 | {0.152f, 0x2c}, | ||
| 93 | {0.159f, 0x2e}, | ||
| 94 | {0.166f, 0x30}, | ||
| 95 | {0.173f, 0x32}, | ||
| 96 | {0.181f, 0x34}, | ||
| 97 | {0.189f, 0x36}, | ||
| 98 | {0.198f, 0x38}, | ||
| 99 | {0.206f, 0x3a}, | ||
| 100 | {0.215f, 0x3c}, | ||
| 101 | {0.225f, 0x3e}, | ||
| 102 | {0.23f, 0x40}, | ||
| 103 | {0.235f, 0x42}, | ||
| 104 | {0.24f, 0x44}, | ||
| 105 | {0.245f, 0x46}, | ||
| 106 | {0.251f, 0x48}, | ||
| 107 | {0.256f, 0x4a}, | ||
| 108 | {0.262f, 0x4c}, | ||
| 109 | {0.268f, 0x4e}, | ||
| 110 | {0.273f, 0x50}, | ||
| 111 | {0.279f, 0x52}, | ||
| 112 | {0.286f, 0x54}, | ||
| 113 | {0.292f, 0x56}, | ||
| 114 | {0.298f, 0x58}, | ||
| 115 | {0.305f, 0x5a}, | ||
| 116 | {0.311f, 0x5c}, | ||
| 117 | {0.318f, 0x5e}, | ||
| 118 | {0.325f, 0x60}, | ||
| 119 | {0.332f, 0x62}, | ||
| 120 | {0.34f, 0x64}, | ||
| 121 | {0.347f, 0x66}, | ||
| 122 | {0.355f, 0x68}, | ||
| 123 | {0.362f, 0x6a}, | ||
| 124 | {0.37f, 0x6c}, | ||
| 125 | {0.378f, 0x6e}, | ||
| 126 | {0.387f, 0x70}, | ||
| 127 | {0.395f, 0x72}, | ||
| 128 | {0.404f, 0x74}, | ||
| 129 | {0.413f, 0x76}, | ||
| 130 | {0.422f, 0x78}, | ||
| 131 | {0.431f, 0x7a}, | ||
| 132 | {0.44f, 0x7c}, | ||
| 133 | {0.45f, 0x7e}, | ||
| 134 | {0.46f, 0x80}, | ||
| 135 | {0.47f, 0x82}, | ||
| 136 | {0.48f, 0x84}, | ||
| 137 | {0.491f, 0x86}, | ||
| 138 | {0.501f, 0x88}, | ||
| 139 | {0.512f, 0x8a}, | ||
| 140 | {0.524f, 0x8c}, | ||
| 141 | {0.535f, 0x8e}, | ||
| 142 | {0.547f, 0x90}, | ||
| 143 | {0.559f, 0x92}, | ||
| 144 | {0.571f, 0x94}, | ||
| 145 | {0.584f, 0x96}, | ||
| 146 | {0.596f, 0x98}, | ||
| 147 | {0.609f, 0x9a}, | ||
| 148 | {0.623f, 0x9c}, | ||
| 149 | {0.636f, 0x9e}, | ||
| 150 | {0.65f, 0xa0}, | ||
| 151 | {0.665f, 0xa2}, | ||
| 152 | {0.679f, 0xa4}, | ||
| 153 | {0.694f, 0xa6}, | ||
| 154 | {0.709f, 0xa8}, | ||
| 155 | {0.725f, 0xaa}, | ||
| 156 | {0.741f, 0xac}, | ||
| 157 | {0.757f, 0xae}, | ||
| 158 | {0.773f, 0xb0}, | ||
| 159 | {0.79f, 0xb2}, | ||
| 160 | {0.808f, 0xb4}, | ||
| 161 | {0.825f, 0xb6}, | ||
| 162 | {0.843f, 0xb8}, | ||
| 163 | {0.862f, 0xba}, | ||
| 164 | {0.881f, 0xbc}, | ||
| 165 | {0.9f, 0xbe}, | ||
| 166 | {0.92f, 0xc0}, | ||
| 167 | {0.94f, 0xc2}, | ||
| 168 | {0.96f, 0xc4}, | ||
| 169 | {0.981f, 0xc6}, | ||
| 170 | {1.003f, 0xc8}, | ||
| 171 | }; | ||
| 172 | |||
| 173 | for (const auto& [amplitude_value, code] : high_fequency_amplitude) { | ||
| 174 | if (amplitude <= amplitude_value) { | ||
| 175 | return static_cast<u8>(code); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); | ||
| 180 | } | ||
| 181 | |||
| 182 | u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const { | ||
| 183 | /* More information about these values can be found here: | ||
| 184 | * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md | ||
| 185 | */ | ||
| 186 | constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ | ||
| 187 | std::pair<f32, int>{0.0f, 0x0040}, | ||
| 188 | {0.01f, 0x8040}, | ||
| 189 | {0.012f, 0x0041}, | ||
| 190 | {0.014f, 0x8041}, | ||
| 191 | {0.017f, 0x0042}, | ||
| 192 | {0.02f, 0x8042}, | ||
| 193 | {0.024f, 0x0043}, | ||
| 194 | {0.028f, 0x8043}, | ||
| 195 | {0.033f, 0x0044}, | ||
| 196 | {0.04f, 0x8044}, | ||
| 197 | {0.047f, 0x0045}, | ||
| 198 | {0.056f, 0x8045}, | ||
| 199 | {0.067f, 0x0046}, | ||
| 200 | {0.08f, 0x8046}, | ||
| 201 | {0.095f, 0x0047}, | ||
| 202 | {0.112f, 0x8047}, | ||
| 203 | {0.117f, 0x0048}, | ||
| 204 | {0.123f, 0x8048}, | ||
| 205 | {0.128f, 0x0049}, | ||
| 206 | {0.134f, 0x8049}, | ||
| 207 | {0.14f, 0x004a}, | ||
| 208 | {0.146f, 0x804a}, | ||
| 209 | {0.152f, 0x004b}, | ||
| 210 | {0.159f, 0x804b}, | ||
| 211 | {0.166f, 0x004c}, | ||
| 212 | {0.173f, 0x804c}, | ||
| 213 | {0.181f, 0x004d}, | ||
| 214 | {0.189f, 0x804d}, | ||
| 215 | {0.198f, 0x004e}, | ||
| 216 | {0.206f, 0x804e}, | ||
| 217 | {0.215f, 0x004f}, | ||
| 218 | {0.225f, 0x804f}, | ||
| 219 | {0.23f, 0x0050}, | ||
| 220 | {0.235f, 0x8050}, | ||
| 221 | {0.24f, 0x0051}, | ||
| 222 | {0.245f, 0x8051}, | ||
| 223 | {0.251f, 0x0052}, | ||
| 224 | {0.256f, 0x8052}, | ||
| 225 | {0.262f, 0x0053}, | ||
| 226 | {0.268f, 0x8053}, | ||
| 227 | {0.273f, 0x0054}, | ||
| 228 | {0.279f, 0x8054}, | ||
| 229 | {0.286f, 0x0055}, | ||
| 230 | {0.292f, 0x8055}, | ||
| 231 | {0.298f, 0x0056}, | ||
| 232 | {0.305f, 0x8056}, | ||
| 233 | {0.311f, 0x0057}, | ||
| 234 | {0.318f, 0x8057}, | ||
| 235 | {0.325f, 0x0058}, | ||
| 236 | {0.332f, 0x8058}, | ||
| 237 | {0.34f, 0x0059}, | ||
| 238 | {0.347f, 0x8059}, | ||
| 239 | {0.355f, 0x005a}, | ||
| 240 | {0.362f, 0x805a}, | ||
| 241 | {0.37f, 0x005b}, | ||
| 242 | {0.378f, 0x805b}, | ||
| 243 | {0.387f, 0x005c}, | ||
| 244 | {0.395f, 0x805c}, | ||
| 245 | {0.404f, 0x005d}, | ||
| 246 | {0.413f, 0x805d}, | ||
| 247 | {0.422f, 0x005e}, | ||
| 248 | {0.431f, 0x805e}, | ||
| 249 | {0.44f, 0x005f}, | ||
| 250 | {0.45f, 0x805f}, | ||
| 251 | {0.46f, 0x0060}, | ||
| 252 | {0.47f, 0x8060}, | ||
| 253 | {0.48f, 0x0061}, | ||
| 254 | {0.491f, 0x8061}, | ||
| 255 | {0.501f, 0x0062}, | ||
| 256 | {0.512f, 0x8062}, | ||
| 257 | {0.524f, 0x0063}, | ||
| 258 | {0.535f, 0x8063}, | ||
| 259 | {0.547f, 0x0064}, | ||
| 260 | {0.559f, 0x8064}, | ||
| 261 | {0.571f, 0x0065}, | ||
| 262 | {0.584f, 0x8065}, | ||
| 263 | {0.596f, 0x0066}, | ||
| 264 | {0.609f, 0x8066}, | ||
| 265 | {0.623f, 0x0067}, | ||
| 266 | {0.636f, 0x8067}, | ||
| 267 | {0.65f, 0x0068}, | ||
| 268 | {0.665f, 0x8068}, | ||
| 269 | {0.679f, 0x0069}, | ||
| 270 | {0.694f, 0x8069}, | ||
| 271 | {0.709f, 0x006a}, | ||
| 272 | {0.725f, 0x806a}, | ||
| 273 | {0.741f, 0x006b}, | ||
| 274 | {0.757f, 0x806b}, | ||
| 275 | {0.773f, 0x006c}, | ||
| 276 | {0.79f, 0x806c}, | ||
| 277 | {0.808f, 0x006d}, | ||
| 278 | {0.825f, 0x806d}, | ||
| 279 | {0.843f, 0x006e}, | ||
| 280 | {0.862f, 0x806e}, | ||
| 281 | {0.881f, 0x006f}, | ||
| 282 | {0.9f, 0x806f}, | ||
| 283 | {0.92f, 0x0070}, | ||
| 284 | {0.94f, 0x8070}, | ||
| 285 | {0.96f, 0x0071}, | ||
| 286 | {0.981f, 0x8071}, | ||
| 287 | {1.003f, 0x0072}, | ||
| 288 | }; | ||
| 289 | |||
| 290 | for (const auto& [amplitude_value, code] : high_fequency_amplitude) { | ||
| 291 | if (amplitude <= amplitude_value) { | ||
| 292 | return static_cast<u16>(code); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); | ||
| 297 | } | ||
| 298 | |||
| 299 | } // 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 000000000..7d0329f03 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/rumble.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 14 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | |||
| 18 | class RumbleProtocol final : private JoyconCommonProtocol { | ||
| 19 | public: | ||
| 20 | RumbleProtocol(std::shared_ptr<JoyconHandle> handle); | ||
| 21 | |||
| 22 | DriverResult EnableRumble(bool is_enabled); | ||
| 23 | |||
| 24 | DriverResult SendVibration(const VibrationValue& vibration); | ||
| 25 | |||
| 26 | private: | ||
| 27 | u16 EncodeHighFrequency(f32 frequency) const; | ||
| 28 | u8 EncodeLowFrequency(f32 frequency) const; | ||
| 29 | u8 EncodeHighAmplitude(f32 amplitude) const; | ||
| 30 | u16 EncodeLowAmplitude(f32 amplitude) const; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace InputCommon::Joycon | ||