input_common: Add support for joycon generic functions
This commit is contained in:
		| @@ -59,6 +59,8 @@ if (ENABLE_SDL2) | |||||||
|         helpers/joycon_driver.h |         helpers/joycon_driver.h | ||||||
|         helpers/joycon_protocol/common_protocol.cpp |         helpers/joycon_protocol/common_protocol.cpp | ||||||
|         helpers/joycon_protocol/common_protocol.h |         helpers/joycon_protocol/common_protocol.h | ||||||
|  |         helpers/joycon_protocol/generic_functions.cpp | ||||||
|  |         helpers/joycon_protocol/generic_functions.h | ||||||
|         helpers/joycon_protocol/joycon_types.h |         helpers/joycon_protocol/joycon_types.h | ||||||
|     ) |     ) | ||||||
|     target_link_libraries(input_common PRIVATE SDL2::SDL2) |     target_link_libraries(input_common PRIVATE SDL2::SDL2) | ||||||
|   | |||||||
| @@ -64,13 +64,24 @@ DriverResult JoyconDriver::InitializeDevice() { | |||||||
|     accelerometer_performance = Joycon::AccelerometerPerformance::HZ100; |     accelerometer_performance = Joycon::AccelerometerPerformance::HZ100; | ||||||
|  |  | ||||||
|     // Initialize HW Protocols |     // Initialize HW Protocols | ||||||
|  |     generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); | ||||||
|  |  | ||||||
|     // Get fixed joycon info |     // Get fixed joycon info | ||||||
|  |     generic_protocol->GetVersionNumber(version); | ||||||
|  |     generic_protocol->GetColor(color); | ||||||
|  |     if (handle_device_type == ControllerType::Pro) { | ||||||
|  |         // Some 3rd party controllers aren't pro controllers | ||||||
|  |         generic_protocol->GetControllerType(device_type); | ||||||
|  |     } else { | ||||||
|  |         device_type = handle_device_type; | ||||||
|  |     } | ||||||
|  |     generic_protocol->GetSerialNumber(serial_number); | ||||||
|     supported_features = GetSupportedFeatures(); |     supported_features = GetSupportedFeatures(); | ||||||
|  |  | ||||||
|     // Get Calibration data |     // Get Calibration data | ||||||
|  |  | ||||||
|     // Set led status |     // Set led status | ||||||
|  |     generic_protocol->SetLedBlinkPattern(static_cast<u8>(1 + port)); | ||||||
|  |  | ||||||
|     // Apply HW configuration |     // Apply HW configuration | ||||||
|     SetPollingMode(); |     SetPollingMode(); | ||||||
| @@ -137,6 +148,9 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { | |||||||
|     case InputReport::SIMPLE_HID_MODE: |     case InputReport::SIMPLE_HID_MODE: | ||||||
|         ReadPassiveMode(buffer); |         ReadPassiveMode(buffer); | ||||||
|         break; |         break; | ||||||
|  |     case InputReport::SUBCMD_REPLY: | ||||||
|  |         LOG_DEBUG(Input, "Unhandled command reply"); | ||||||
|  |         break; | ||||||
|     default: |     default: | ||||||
|         LOG_ERROR(Input, "Report mode not Implemented {}", report_mode); |         LOG_ERROR(Input, "Report mode not Implemented {}", report_mode); | ||||||
|         break; |         break; | ||||||
| @@ -145,6 +159,30 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { | |||||||
|  |  | ||||||
| void JoyconDriver::SetPollingMode() { | void JoyconDriver::SetPollingMode() { | ||||||
|     disable_input_thread = true; |     disable_input_thread = true; | ||||||
|  |  | ||||||
|  |     if (motion_enabled && supported_features.motion) { | ||||||
|  |         generic_protocol->EnableImu(true); | ||||||
|  |         generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance, | ||||||
|  |                                        accelerometer_sensitivity, accelerometer_performance); | ||||||
|  |     } else { | ||||||
|  |         generic_protocol->EnableImu(false); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (passive_enabled && supported_features.passive) { | ||||||
|  |         const auto result = generic_protocol->EnablePassiveMode(); | ||||||
|  |         if (result == DriverResult::Success) { | ||||||
|  |             disable_input_thread = false; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         LOG_ERROR(Input, "Error enabling passive mode"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Default Mode | ||||||
|  |     const auto result = generic_protocol->EnableActiveMode(); | ||||||
|  |     if (result != DriverResult::Success) { | ||||||
|  |         LOG_ERROR(Input, "Error enabling active mode"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     disable_input_thread = false; |     disable_input_thread = false; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -257,15 +295,22 @@ bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) { | |||||||
|  |  | ||||||
| DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { | DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { | ||||||
|     std::scoped_lock lock{mutex}; |     std::scoped_lock lock{mutex}; | ||||||
|  |     if (disable_input_thread) { | ||||||
|  |         return DriverResult::HandleInUse; | ||||||
|  |     } | ||||||
|     return DriverResult::NotSupported; |     return DriverResult::NotSupported; | ||||||
| } | } | ||||||
|  |  | ||||||
| DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { | DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { | ||||||
|     std::scoped_lock lock{mutex}; |     std::scoped_lock lock{mutex}; | ||||||
|     return DriverResult::NotSupported; |     if (disable_input_thread) { | ||||||
|  |         return DriverResult::HandleInUse; | ||||||
|  |     } | ||||||
|  |     return generic_protocol->SetLedPattern(led_pattern); | ||||||
| } | } | ||||||
|  |  | ||||||
| DriverResult JoyconDriver::SetPasiveMode() { | DriverResult JoyconDriver::SetPasiveMode() { | ||||||
|  |     std::scoped_lock lock{mutex}; | ||||||
|     motion_enabled = false; |     motion_enabled = false; | ||||||
|     hidbus_enabled = false; |     hidbus_enabled = false; | ||||||
|     nfc_enabled = false; |     nfc_enabled = false; | ||||||
| @@ -275,7 +320,8 @@ DriverResult JoyconDriver::SetPasiveMode() { | |||||||
| } | } | ||||||
|  |  | ||||||
| DriverResult JoyconDriver::SetActiveMode() { | DriverResult JoyconDriver::SetActiveMode() { | ||||||
|     motion_enabled = false; |     std::scoped_lock lock{mutex}; | ||||||
|  |     motion_enabled = true; | ||||||
|     hidbus_enabled = false; |     hidbus_enabled = false; | ||||||
|     nfc_enabled = false; |     nfc_enabled = false; | ||||||
|     passive_enabled = false; |     passive_enabled = false; | ||||||
| @@ -284,6 +330,7 @@ DriverResult JoyconDriver::SetActiveMode() { | |||||||
| } | } | ||||||
|  |  | ||||||
| DriverResult JoyconDriver::SetNfcMode() { | DriverResult JoyconDriver::SetNfcMode() { | ||||||
|  |     std::scoped_lock lock{mutex}; | ||||||
|     motion_enabled = false; |     motion_enabled = false; | ||||||
|     hidbus_enabled = false; |     hidbus_enabled = false; | ||||||
|     nfc_enabled = true; |     nfc_enabled = true; | ||||||
| @@ -293,6 +340,7 @@ DriverResult JoyconDriver::SetNfcMode() { | |||||||
| } | } | ||||||
|  |  | ||||||
| DriverResult JoyconDriver::SetRingConMode() { | DriverResult JoyconDriver::SetRingConMode() { | ||||||
|  |     std::scoped_lock lock{mutex}; | ||||||
|     motion_enabled = true; |     motion_enabled = true; | ||||||
|     hidbus_enabled = true; |     hidbus_enabled = true; | ||||||
|     nfc_enabled = false; |     nfc_enabled = false; | ||||||
| @@ -328,7 +376,7 @@ std::size_t JoyconDriver::GetDevicePort() const { | |||||||
|  |  | ||||||
| ControllerType JoyconDriver::GetDeviceType() const { | ControllerType JoyconDriver::GetDeviceType() const { | ||||||
|     std::scoped_lock lock{mutex}; |     std::scoped_lock lock{mutex}; | ||||||
|     return handle_device_type; |     return device_type; | ||||||
| } | } | ||||||
|  |  | ||||||
| ControllerType JoyconDriver::GetHandleDeviceType() const { | ControllerType JoyconDriver::GetHandleDeviceType() const { | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| #include <span> | #include <span> | ||||||
| #include <thread> | #include <thread> | ||||||
|  |  | ||||||
|  | #include "input_common/helpers/joycon_protocol/generic_functions.h" | ||||||
| #include "input_common/helpers/joycon_protocol/joycon_types.h" | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||||||
|  |  | ||||||
| namespace InputCommon::Joycon { | namespace InputCommon::Joycon { | ||||||
| @@ -94,6 +95,7 @@ private: | |||||||
|     void ReadNfcIRMode(std::span<u8> buffer); |     void ReadNfcIRMode(std::span<u8> buffer); | ||||||
|  |  | ||||||
|     // Protocol Features |     // Protocol Features | ||||||
|  |     std::unique_ptr<GenericProtocol> generic_protocol = nullptr; | ||||||
|  |  | ||||||
|     // Connection status |     // Connection status | ||||||
|     bool is_connected{}; |     bool is_connected{}; | ||||||
|   | |||||||
							
								
								
									
										147
									
								
								src/input_common/helpers/joycon_protocol/generic_functions.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/input_common/helpers/joycon_protocol/generic_functions.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | |||||||
|  | // 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/generic_functions.h" | ||||||
|  |  | ||||||
|  | namespace InputCommon::Joycon { | ||||||
|  |  | ||||||
|  | GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle) | ||||||
|  |     : JoyconCommonProtocol(handle) {} | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::EnablePassiveMode() { | ||||||
|  |     SetBlocking(); | ||||||
|  |     const auto result = SetReportMode(ReportMode::SIMPLE_HID_MODE); | ||||||
|  |     SetNonBlocking(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::EnableActiveMode() { | ||||||
|  |     SetBlocking(); | ||||||
|  |     const auto result = SetReportMode(ReportMode::STANDARD_FULL_60HZ); | ||||||
|  |     SetNonBlocking(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { | ||||||
|  |     std::vector<u8> output; | ||||||
|  |     SetBlocking(); | ||||||
|  |  | ||||||
|  |     const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); | ||||||
|  |  | ||||||
|  |     device_info = {}; | ||||||
|  |     if (result == DriverResult::Success) { | ||||||
|  |         memcpy(&device_info, output.data(), sizeof(DeviceInfo)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SetNonBlocking(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) { | ||||||
|  |     return GetDeviceType(controller_type); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::EnableImu(bool enable) { | ||||||
|  |     const std::vector<u8> buffer{static_cast<u8>(enable ? 1 : 0)}; | ||||||
|  |     std::vector<u8> output; | ||||||
|  |     SetBlocking(); | ||||||
|  |     const auto result = SendSubCommand(SubCommand::ENABLE_IMU, buffer, output); | ||||||
|  |     SetNonBlocking(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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)}; | ||||||
|  |     std::vector<u8> output; | ||||||
|  |     SetBlocking(); | ||||||
|  |     const auto result = SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer, output); | ||||||
|  |     SetNonBlocking(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::GetBattery(u32& battery_level) { | ||||||
|  |     battery_level = 0; | ||||||
|  |     return DriverResult::NotSupported; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::GetColor(Color& color) { | ||||||
|  |     std::vector<u8> buffer; | ||||||
|  |     SetBlocking(); | ||||||
|  |     const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); | ||||||
|  |     SetNonBlocking(); | ||||||
|  |  | ||||||
|  |     color = {}; | ||||||
|  |     if (result == DriverResult::Success) { | ||||||
|  |         color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]); | ||||||
|  |         color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]); | ||||||
|  |         color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]); | ||||||
|  |         color.right_grip = static_cast<u32>((buffer[9] << 16) | (buffer[10] << 8) | buffer[11]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { | ||||||
|  |     std::vector<u8> buffer; | ||||||
|  |     SetBlocking(); | ||||||
|  |     const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); | ||||||
|  |     SetNonBlocking(); | ||||||
|  |  | ||||||
|  |     serial_number = {}; | ||||||
|  |     if (result == DriverResult::Success) { | ||||||
|  |         memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::GetTemperature(u32& temperature) { | ||||||
|  |     // Not all devices have temperature sensor | ||||||
|  |     temperature = 25; | ||||||
|  |     return DriverResult::NotSupported; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) { | ||||||
|  |     DeviceInfo device_info{}; | ||||||
|  |  | ||||||
|  |     const auto result = GetDeviceInfo(device_info); | ||||||
|  |     version = device_info.firmware; | ||||||
|  |  | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::SetHomeLight() { | ||||||
|  |     const std::vector<u8> buffer{0x0f, 0xf0, 0x00}; | ||||||
|  |     std::vector<u8> output; | ||||||
|  |     SetBlocking(); | ||||||
|  |  | ||||||
|  |     const auto result = SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer, output); | ||||||
|  |  | ||||||
|  |     SetNonBlocking(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::SetLedBusy() { | ||||||
|  |     return DriverResult::NotSupported; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::SetLedPattern(u8 leds) { | ||||||
|  |     const std::vector<u8> buffer{leds}; | ||||||
|  |     std::vector<u8> output; | ||||||
|  |     SetBlocking(); | ||||||
|  |  | ||||||
|  |     const auto result = SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer, output); | ||||||
|  |  | ||||||
|  |     SetNonBlocking(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) { | ||||||
|  |     return SetLedPattern(static_cast<u8>(leds << 4)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace InputCommon::Joycon | ||||||
							
								
								
									
										108
									
								
								src/input_common/helpers/joycon_protocol/generic_functions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/input_common/helpers/joycon_protocol/generic_functions.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | // 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 "input_common/helpers/joycon_protocol/common_protocol.h" | ||||||
|  | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||||||
|  |  | ||||||
|  | namespace InputCommon::Joycon { | ||||||
|  |  | ||||||
|  | /// Joycon driver functions that easily implemented | ||||||
|  | class GenericProtocol final : private JoyconCommonProtocol { | ||||||
|  | public: | ||||||
|  |     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 | ||||||
|  |     DriverResult EnablePassiveMode(); | ||||||
|  |  | ||||||
|  |     /// Enables active mode. This mode will return the current status every 5-15ms | ||||||
|  |     DriverResult EnableActiveMode(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Sends a request to obtain the joycon firmware and mac from handle | ||||||
|  |      * @returns controller device info | ||||||
|  |      */ | ||||||
|  |     DriverResult GetDeviceInfo(DeviceInfo& controller_type); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Sends a request to obtain the joycon type from handle | ||||||
|  |      * @returns controller type of the joycon | ||||||
|  |      */ | ||||||
|  |     DriverResult GetControllerType(ControllerType& controller_type); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Enables motion input | ||||||
|  |      * @param enable if true motion data will be enabled | ||||||
|  |      */ | ||||||
|  |     DriverResult EnableImu(bool enable); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Configures the motion sensor with the specified parameters | ||||||
|  |      * @param gsen gyroscope sensor sensitvity in degrees per second | ||||||
|  |      * @param gfrec gyroscope sensor frequency in hertz | ||||||
|  |      * @param asen accelerometer sensitivity in G force | ||||||
|  |      * @param afrec accelerometer frequency in hertz | ||||||
|  |      */ | ||||||
|  |     DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, | ||||||
|  |                               AccelerometerSensitivity asen, AccelerometerPerformance afrec); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Request battery level from the device | ||||||
|  |      * @returns battery level | ||||||
|  |      */ | ||||||
|  |     DriverResult GetBattery(u32& battery_level); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Request joycon colors from the device | ||||||
|  |      * @returns colors of the body and buttons | ||||||
|  |      */ | ||||||
|  |     DriverResult GetColor(Color& color); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Request joycon serial number from the device | ||||||
|  |      * @returns 16 byte serial number | ||||||
|  |      */ | ||||||
|  |     DriverResult GetSerialNumber(SerialNumber& serial_number); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Request joycon serial number from the device | ||||||
|  |      * @returns 16 byte serial number | ||||||
|  |      */ | ||||||
|  |     DriverResult GetTemperature(u32& temperature); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Request joycon serial number from the device | ||||||
|  |      * @returns 16 byte serial number | ||||||
|  |      */ | ||||||
|  |     DriverResult GetVersionNumber(FirmwareVersion& version); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Sets home led behaviour | ||||||
|  |      */ | ||||||
|  |     DriverResult SetHomeLight(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Sets home led into a slow breathing state | ||||||
|  |      */ | ||||||
|  |     DriverResult SetLedBusy(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Sets the 4 player leds on the joycon on a solid state | ||||||
|  |      * @params bit flag containing the led state | ||||||
|  |      */ | ||||||
|  |     DriverResult SetLedPattern(u8 leds); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Sets the 4 player leds on the joycon on a blinking state | ||||||
|  |      * @returns bit flag containing the led state | ||||||
|  |      */ | ||||||
|  |     DriverResult SetLedBlinkPattern(u8 leds); | ||||||
|  | }; | ||||||
|  | } // namespace InputCommon::Joycon | ||||||
		Reference in New Issue
	
	Block a user