diff options
Diffstat (limited to 'src/core/hle/service/hid')
-rw-r--r-- | src/core/hle/service/hid/controllers/npad.cpp | 307 | ||||
-rw-r--r-- | src/core/hle/service/hid/controllers/npad.h | 149 | ||||
-rw-r--r-- | src/core/hle/service/hid/errors.h | 4 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid.cpp | 160 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus.cpp | 531 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus.h | 131 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/hidbus_base.cpp | 72 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/hidbus_base.h | 179 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/ringcon.cpp | 286 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/ringcon.h | 254 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/starlink.cpp | 51 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/starlink.h | 39 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/stubbed.cpp | 52 | ||||
-rw-r--r-- | src/core/hle/service/hid/hidbus/stubbed.h | 39 |
15 files changed, 2069 insertions, 186 deletions
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e5c951e06..68407e39a 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -17,6 +17,7 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/kernel_helpers.h" namespace Service::HID { @@ -48,15 +49,17 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { } bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { - return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && - device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && - device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + return npad_id && npad_type && device_index; } bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { - return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && - device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && - device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + return npad_id && npad_type && device_index; } Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, @@ -262,11 +265,6 @@ void Controller_NPad::OnInit() { service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); } - if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) { - // We want to support all controllers - hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); - } - supported_npad_id_types.resize(npad_id_list.size()); std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), npad_id_list.size() * sizeof(Core::HID::NpadIdType)); @@ -288,14 +286,6 @@ void Controller_NPad::OnInit() { WriteEmptyEntry(npad); } } - - // Connect controllers - for (auto& controller : controller_data) { - const auto& device = controller.device; - if (device->IsConnected()) { - AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); - } - } } void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { @@ -320,6 +310,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { } void Controller_NPad::OnRelease() { + is_controller_initialized = false; for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; service_context.CloseEvent(controller.styleset_changed_event); @@ -330,7 +321,7 @@ void Controller_NPad::OnRelease() { } void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; auto& controller = GetControllerFromNpadIdType(npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); if (!controller.device->IsConnected()) { @@ -651,9 +642,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { hid_core.SetSupportedStyleTag(style_set); + + if (is_controller_initialized) { + return; + } + + // Once SetSupportedStyleSet is called controllers are fully initialized + is_controller_initialized = true; + + // Connect all active controllers + for (auto& controller : controller_data) { + const auto& device = controller.device; + if (device->IsConnected()) { + AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); + } + } } Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { + if (!is_controller_initialized) { + return {Core::HID::NpadStyleSet::None}; + } return hid_core.GetSupportedStyleTag(); } @@ -1001,87 +1010,271 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { WriteEmptyEntry(controller.shared_memory_entry); } -void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, - GyroscopeZeroDriftMode drift_mode) { +ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, + GyroscopeZeroDriftMode drift_mode) { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - return; + return NpadInvalidHandle; + } + + auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode; + break; + case Core::HID::NpadStyleIndex::Handheld: + controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode; + break; + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::GameCube: + case Core::HID::NpadStyleIndex::Pokeball: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode; + break; + } + controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode; + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode; + break; + case Core::HID::NpadStyleIndex::JoyconRight: + controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode; + break; + default: + LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type); + return NpadInvalidHandle; + } + + return ResultSuccess; +} + +ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, + GyroscopeZeroDriftMode& drift_mode) const { + if (!IsDeviceHandleValid(sixaxis_handle)) { + LOG_ERROR(Service_HID, "Invalid handle"); + return NpadInvalidHandle; } + auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.gyroscope_zero_drift_mode = drift_mode; + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode; + break; + case Core::HID::NpadStyleIndex::Handheld: + drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode; + break; + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::GameCube: + case Core::HID::NpadStyleIndex::Pokeball: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode; + break; + } + drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode; + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode; + break; + case Core::HID::NpadStyleIndex::JoyconRight: + drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode; + break; + default: + LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type); + return NpadInvalidHandle; + } + + return ResultSuccess; } -Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode( - Core::HID::SixAxisSensorHandle sixaxis_handle) const { +ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle, + bool& is_at_rest) const { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - // Return the default value - return GyroscopeZeroDriftMode::Standard; + return NpadInvalidHandle; } const auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.gyroscope_zero_drift_mode; + is_at_rest = controller.sixaxis_at_rest; + return ResultSuccess; } -bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const { +ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( + Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - // Return the default value - return true; + return NpadInvalidHandle; } - const auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.sixaxis_at_rest; + + // We don't support joycon firmware updates + is_firmware_available = false; + return ResultSuccess; } -void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_status) { +ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, + bool sixaxis_status) { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - return; + return NpadInvalidHandle; } auto& controller = GetControllerFromHandle(sixaxis_handle); controller.sixaxis_sensor_enabled = sixaxis_status; + return ResultSuccess; } -void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_fusion_status) { +ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled( + Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - return; + return NpadInvalidHandle; } + auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_fusion_enabled = sixaxis_fusion_status; -} + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::Handheld: + is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::GameCube: + case Core::HID::NpadStyleIndex::Pokeball: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled; + break; + } + is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::JoyconRight: + is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled; + break; + default: + LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type); + return NpadInvalidHandle; + } -void Controller_NPad::SetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle, - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { + return ResultSuccess; +} +ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, + bool is_fusion_enabled) { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - return; + return NpadInvalidHandle; } + auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_fusion = sixaxis_fusion_parameters; + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::Handheld: + controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::GameCube: + case Core::HID::NpadStyleIndex::Pokeball: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled; + break; + } + controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled; + break; + case Core::HID::NpadStyleIndex::JoyconRight: + controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled; + break; + default: + LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type); + return NpadInvalidHandle; + } + + return ResultSuccess; } -Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle) { +ResultCode Controller_NPad::SetSixAxisFusionParameters( + Core::HID::SixAxisSensorHandle sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - // Since these parameters are unknow just return zeros - return {}; + return NpadInvalidHandle; + } + const auto param1 = sixaxis_fusion_parameters.parameter1; + if (param1 < 0.0f || param1 > 1.0f) { + return InvalidSixAxisFusionRange; } + auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.sixaxis_fusion; + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters; + break; + case Core::HID::NpadStyleIndex::Handheld: + controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters; + break; + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::GameCube: + case Core::HID::NpadStyleIndex::Pokeball: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters; + break; + } + controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters; + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + controller.sixaxis_left.fusion = sixaxis_fusion_parameters; + break; + case Core::HID::NpadStyleIndex::JoyconRight: + controller.sixaxis_right.fusion = sixaxis_fusion_parameters; + break; + default: + LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type); + return NpadInvalidHandle; + } + + return ResultSuccess; } -void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) { +ResultCode Controller_NPad::GetSixAxisFusionParameters( + Core::HID::SixAxisSensorHandle sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const { if (!IsDeviceHandleValid(sixaxis_handle)) { LOG_ERROR(Service_HID, "Invalid handle"); - return; + return NpadInvalidHandle; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - // Since these parameters are unknow just fill with zeros - controller.sixaxis_fusion = {}; + + const auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + parameters = controller.sixaxis_fullkey.fusion; + break; + case Core::HID::NpadStyleIndex::Handheld: + parameters = controller.sixaxis_handheld.fusion; + break; + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::GameCube: + case Core::HID::NpadStyleIndex::Pokeball: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + parameters = controller.sixaxis_dual_left.fusion; + break; + } + parameters = controller.sixaxis_dual_right.fusion; + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + parameters = controller.sixaxis_left.fusion; + break; + case Core::HID::NpadStyleIndex::JoyconRight: + parameters = controller.sixaxis_right.fusion; + break; + default: + LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type); + return NpadInvalidHandle; + } + + return ResultSuccess; } void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 3287cf435..f7313711f 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -28,7 +28,9 @@ class KReadableEvent; namespace Service::KernelHelpers { class ServiceContext; -} +} // namespace Service::KernelHelpers + +union ResultCode; namespace Service::HID { @@ -143,20 +145,26 @@ public: void DisconnectNpad(Core::HID::NpadIdType npad_id); - void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, - GyroscopeZeroDriftMode drift_mode); - GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode( - Core::HID::SixAxisSensorHandle sixaxis_handle) const; - bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const; - void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status); - void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_fusion_status); - void SetSixAxisFusionParameters( + ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, + GyroscopeZeroDriftMode drift_mode); + ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, + GyroscopeZeroDriftMode& drift_mode) const; + ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle, + bool& is_at_rest) const; + ResultCode IsFirmwareUpdateAvailableForSixAxisSensor( + Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const; + ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, + bool sixaxis_status); + ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, + bool& is_fusion_enabled) const; + ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, + bool is_fusion_enabled); + ResultCode SetSixAxisFusionParameters( Core::HID::SixAxisSensorHandle sixaxis_handle, Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); - Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle); - void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle); + ResultCode GetSixAxisFusionParameters( + Core::HID::SixAxisSensorHandle sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const; Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, @@ -191,16 +199,16 @@ private: // This is nn::hid::detail::NpadFullKeyColorState struct NpadFullKeyColorState { - ColorAttribute attribute; - Core::HID::NpadControllerColor fullkey; + ColorAttribute attribute{ColorAttribute::NoController}; + Core::HID::NpadControllerColor fullkey{}; }; static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); // This is nn::hid::detail::NpadJoyColorState struct NpadJoyColorState { - ColorAttribute attribute; - Core::HID::NpadControllerColor left; - Core::HID::NpadControllerColor right; + ColorAttribute attribute{ColorAttribute::NoController}; + Core::HID::NpadControllerColor left{}; + Core::HID::NpadControllerColor right{}; }; static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); @@ -226,11 +234,11 @@ private: // This is nn::hid::NpadPalmaState // This is nn::hid::NpadSystemExtState struct NPadGenericState { - s64_le sampling_number; - Core::HID::NpadButtonState npad_buttons; - Core::HID::AnalogStickState l_stick; - Core::HID::AnalogStickState r_stick; - NpadAttribute connection_status; + s64_le sampling_number{}; + Core::HID::NpadButtonState npad_buttons{}; + Core::HID::AnalogStickState l_stick{}; + Core::HID::AnalogStickState r_stick{}; + NpadAttribute connection_status{}; INSERT_PADDING_BYTES(4); // Reserved }; static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); @@ -253,7 +261,7 @@ private: Common::Vec3f gyro{}; Common::Vec3f rotation{}; std::array<Common::Vec3f, 3> orientation{}; - SixAxisSensorAttribute attribute; + SixAxisSensorAttribute attribute{}; INSERT_PADDING_BYTES(4); // Reserved }; static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); @@ -325,11 +333,11 @@ private: // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl struct NfcXcdDeviceHandleStateImpl { - u64 handle; - bool is_available; - bool is_activated; + u64 handle{}; + bool is_available{}; + bool is_activated{}; INSERT_PADDING_BYTES(0x6); // Reserved - u64 sampling_number; + u64 sampling_number{}; }; static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, "NfcXcdDeviceHandleStateImpl is an invalid size"); @@ -366,8 +374,8 @@ private: }; struct AppletFooterUi { - AppletFooterUiAttributes attributes; - AppletFooterUiType type; + AppletFooterUiAttributes attributes{}; + AppletFooterUiType type{AppletFooterUiType::None}; INSERT_PADDING_BYTES(0x5B); // Reserved }; static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); @@ -404,41 +412,41 @@ private: // This is nn::hid::detail::NpadInternalState struct NpadInternalState { - Core::HID::NpadStyleTag style_tag; - NpadJoyAssignmentMode assignment_mode; - NpadFullKeyColorState fullkey_color; - NpadJoyColorState joycon_color; - Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; - Lifo<NPadGenericState, hid_entry_count> handheld_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; - Lifo<NPadGenericState, hid_entry_count> palma_lifo; - Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; - DeviceType device_type; + Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; + NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; + NpadFullKeyColorState fullkey_color{}; + NpadJoyColorState joycon_color{}; + Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{}; + Lifo<NPadGenericState, hid_entry_count> handheld_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; + Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; + Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; + DeviceType device_type{}; INSERT_PADDING_BYTES(0x4); // Reserved - NPadSystemProperties system_properties; - NpadSystemButtonProperties button_properties; - Core::HID::NpadBatteryLevel battery_level_dual; - Core::HID::NpadBatteryLevel battery_level_left; - Core::HID::NpadBatteryLevel battery_level_right; + NPadSystemProperties system_properties{}; + NpadSystemButtonProperties button_properties{}; + Core::HID::NpadBatteryLevel battery_level_dual{}; + Core::HID::NpadBatteryLevel battery_level_left{}; + Core::HID::NpadBatteryLevel battery_level_right{}; union { - Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; - AppletFooterUi applet_footer; + AppletFooterUi applet_footer{}; + Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo; }; INSERT_PADDING_BYTES(0x20); // Unknown - Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; - NpadLarkType lark_type_l_and_main; - NpadLarkType lark_type_r; - NpadLuciaType lucia_type; - NpadLagonType lagon_type; - NpadLagerType lager_type; + Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{}; + NpadLarkType lark_type_l_and_main{}; + NpadLarkType lark_type_r{}; + NpadLuciaType lucia_type{}; + NpadLagonType lagon_type{}; + NpadLagerType lager_type{}; // FW 13.x Investigate there is some sort of bitflag related to joycons INSERT_PADDING_BYTES(0x4); INSERT_PADDING_BYTES(0xc08); // Unknown @@ -451,6 +459,12 @@ private: std::chrono::steady_clock::time_point last_vibration_timepoint{}; }; + struct SixaxisParameters { + bool is_fusion_enabled{true}; + Core::HID::SixAxisSensorFusionParameters fusion{}; + GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; + }; + struct NpadControllerData { Core::HID::EmulatedController* device; Kernel::KEvent* styleset_changed_event{}; @@ -467,9 +481,12 @@ private: // Motion parameters bool sixaxis_at_rest{true}; bool sixaxis_sensor_enabled{true}; - bool sixaxis_fusion_enabled{false}; - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{}; - GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; + SixaxisParameters sixaxis_fullkey{}; + SixaxisParameters sixaxis_handheld{}; + SixaxisParameters sixaxis_dual_left{}; + SixaxisParameters sixaxis_dual_right{}; + SixaxisParameters sixaxis_left{}; + SixaxisParameters sixaxis_right{}; // Current pad state NPadGenericState npad_pad_state{}; @@ -481,6 +498,7 @@ private: SixAxisSensorState sixaxis_dual_right_state{}; SixAxisSensorState sixaxis_left_lifo_state{}; SixAxisSensorState sixaxis_right_lifo_state{}; + int callback_key; }; @@ -511,7 +529,8 @@ private: NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; bool permit_vibration_session_enabled{false}; - bool analog_stick_use_center_clamp{}; + bool analog_stick_use_center_clamp{false}; bool is_in_lr_assignment_mode{false}; + bool is_controller_initialized{false}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 3583642e7..7c5fb3e52 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -8,6 +8,8 @@ namespace Service::HID { -constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; +constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100}; +constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423}; +constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710}; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d9202ea6c..baf21df62 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" #include "core/memory.h" @@ -247,7 +248,7 @@ Hid::Hid(Core::System& system_) {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, - {68, nullptr, "IsSixAxisSensorFusionEnabled"}, + {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, @@ -527,8 +528,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisEnabled(parameters.sixaxis_handle, true); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -536,7 +537,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { @@ -550,8 +551,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisEnabled(parameters.sixaxis_handle, false); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -559,7 +560,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); +} + +void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_enabled{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_enabled); } void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { @@ -574,9 +601,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisFusionEnabled(parameters.sixaxis_handle, - parameters.enable_sixaxis_sensor_fusion); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, + parameters.enable_sixaxis_sensor_fusion); LOG_DEBUG(Service_HID, "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " @@ -586,7 +613,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -601,8 +628,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " @@ -612,7 +640,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -626,9 +654,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - const auto sixaxis_fusion_parameters = - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetSixAxisFusionParameters(parameters.sixaxis_handle); + Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; + const auto& controller = + GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -636,8 +666,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushRaw(sixaxis_fusion_parameters); + rb.Push(result); + rb.PushRaw(fusion_parameters); } void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -651,8 +681,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .ResetSixAxisFusionParameters(parameters.sixaxis_handle); + // Since these parameters are unknow just use what HW outputs + const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ + .parameter1 = 0.03f, + .parameter2 = 0.4f, + }; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result1 = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); + const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -660,6 +697,14 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; + if (result1.IsError()) { + rb.Push(result1); + return; + } + if (result2.IsError()) { + rb.Push(result2); + return; + } rb.Push(ResultSuccess); } @@ -669,8 +714,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " @@ -679,7 +724,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { drift_mode, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -693,15 +738,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; + auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle)); + rb.Push(result); + rb.PushEnum(drift_mode); } void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -714,10 +762,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); const auto parameters{rp.PopRaw<Parameters>()}; - const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -725,7 +773,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { @@ -739,15 +787,18 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; + bool is_at_rest{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .IsSixAxisSensorAtRest(parameters.sixaxis_handle)); + rb.Push(result); + rb.Push(is_at_rest); } void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { @@ -761,6 +812,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c const auto parameters{rp.PopRaw<Parameters>()}; + bool is_firmware_available{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor( + parameters.sixaxis_handle, is_firmware_available); + LOG_WARNING( Service_HID, "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -768,8 +824,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(false); + rb.Push(result); + rb.Push(is_firmware_available); } void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { @@ -878,6 +934,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); + // Games expect this event to be signaled after calling this function + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SignalStyleSetChangedEvent(parameters.npad_id); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) @@ -1115,7 +1175,7 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } else { LOG_ERROR(Service_HID, "Npads are not connected!"); - rb.Push(ERR_NPAD_NOT_CONNECTED); + rb.Push(NpadNotConnected); } } @@ -2124,32 +2184,6 @@ public: } }; -class HidBus final : public ServiceFramework<HidBus> { -public: - explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} { - // clang-format off - static const FunctionInfo functions[] = { - {1, nullptr, "GetBusHandle"}, - {2, nullptr, "IsExternalDeviceConnected"}, - {3, nullptr, "Initialize"}, - {4, nullptr, "Finalize"}, - {5, nullptr, "EnableExternalDevice"}, - {6, nullptr, "GetExternalDeviceId"}, - {7, nullptr, "SendCommandAsync"}, - {8, nullptr, "GetSendCommandAsynceResult"}, - {9, nullptr, "SetEventForSendCommandAsycResult"}, - {10, nullptr, "GetSharedMemoryHandle"}, - {11, nullptr, "EnableJoyPollingReceiveMode"}, - {12, nullptr, "DisableJoyPollingReceiveMode"}, - {13, nullptr, "GetPollingData"}, - {14, nullptr, "SetStatusManagerType"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { std::make_shared<Hid>(system)->InstallAsService(service_manager); std::make_shared<HidBus>(system)->InstallAsService(service_manager); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c281081a7..666cb6ff5 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -103,6 +103,7 @@ private: void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); + void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx); void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp new file mode 100644 index 000000000..af7662a15 --- /dev/null +++ b/src/core/hle/service/hid/hidbus.cpp @@ -0,0 +1,531 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/hid/hidbus.h" +#include "core/hle/service/hid/hidbus/ringcon.h" +#include "core/hle/service/hid/hidbus/starlink.h" +#include "core/hle/service/hid/hidbus/stubbed.h" +#include "core/hle/service/service.h" +#include "core/memory.h" + +namespace Service::HID { +// (15ms, 66Hz) +constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; + +HidBus::HidBus(Core::System& system_) + : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} { + + // clang-format off + static const FunctionInfo functions[] = { + {1, &HidBus::GetBusHandle, "GetBusHandle"}, + {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"}, + {3, &HidBus::Initialize, "Initialize"}, + {4, &HidBus::Finalize, "Finalize"}, + {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"}, + {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"}, + {7, &HidBus::SendCommandAsync, "SendCommandAsync"}, + {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"}, + {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"}, + {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, + {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"}, + {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"}, + {13, nullptr, "GetPollingData"}, + {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"}, + }; + // clang-format on + + RegisterHandlers(functions); + + // Register update callbacks + hidbus_update_event = Core::Timing::CreateEvent( + "Hidbus::UpdateCallback", + [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + const auto guard = LockService(); + UpdateHidbus(user_data, ns_late); + }); + + system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event); +} + +HidBus::~HidBus() { + system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0); +} + +void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + + if (is_hidbus_enabled) { + for (std::size_t i = 0; i < devices.size(); ++i) { + if (!devices[i].is_device_initializated) { + continue; + } + auto& device = devices[i].device; + device->OnUpdate(); + auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index]; + cur_entry.is_polling_mode = device->IsPollingMode(); + cur_entry.polling_mode = device->GetPollingMode(); + cur_entry.is_enabled = device->IsEnabled(); + + u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer(); + std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status, + sizeof(HidbusStatusManagerEntry)); + } + } + + // If ns_late is higher than the update rate ignore the delay + if (ns_late > hidbus_update_ns) { + ns_late = {}; + } + + core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event); +} + +std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { + for (std::size_t i = 0; i < devices.size(); ++i) { + const auto& device_handle = devices[i].handle; + if (handle.abstracted_pad_id == device_handle.abstracted_pad_id && + handle.internal_index == device_handle.internal_index && + handle.player_number == device_handle.player_number && + handle.bus_type == device_handle.bus_type && + handle.is_valid == device_handle.is_valid) { + return i; + } + } + return std::nullopt; +} + +void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + BusType bus_type; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", + parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id); + + bool is_handle_found = 0; + std::size_t handle_index = 0; + + for (std::size_t i = 0; i < devices.size(); i++) { + const auto& handle = devices[i].handle; + if (!handle.is_valid) { + continue; + } + if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id && + handle.bus_type == parameters.bus_type) { + is_handle_found = true; + handle_index = i; + break; + } + } + + // Handle not found. Create a new one + if (!is_handle_found) { + for (std::size_t i = 0; i < devices.size(); i++) { + if (devices[i].handle.is_valid) { + continue; + } + devices[i].handle = { + .abstracted_pad_id = static_cast<u8>(i), + .internal_index = static_cast<u8>(i), + .player_number = static_cast<u8>(parameters.npad_id), + .bus_type = parameters.bus_type, + .is_valid = true, + }; + handle_index = i; + break; + } + } + + struct OutData { + bool is_valid; + INSERT_PADDING_BYTES(7); + BusHandle handle; + }; + static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size."); + + const OutData out_data{ + .is_valid = true, + .handle = devices[handle_index].handle, + }; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(out_data); +} + +void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + const bool is_attached = device->IsDeviceActivated(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::Initialize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={} bus_type={} internal_index={} " + "player_number={} is_valid={}, applet_resource_user_id={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); + + is_hidbus_enabled = true; + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto entry_index = devices[device_index.value()].handle.internal_index; + auto& cur_entry = hidbus_status.entries[entry_index]; + + if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { + MakeDevice<RingController>(bus_handle_); + devices[device_index.value()].is_device_initializated = true; + devices[device_index.value()].device->ActivateDevice(); + cur_entry.is_in_focus = true; + cur_entry.is_connected = true; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + } else { + MakeDevice<HidbusStubbed>(bus_handle_); + devices[device_index.value()].is_device_initializated = true; + cur_entry.is_in_focus = true; + cur_entry.is_connected = false; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + } + + std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, + sizeof(hidbus_status)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::Finalize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}, applet_resource_user_id={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto entry_index = devices[device_index.value()].handle.internal_index; + auto& cur_entry = hidbus_status.entries[entry_index]; + auto& device = devices[device_index.value()].device; + devices[device_index.value()].is_device_initializated = false; + device->DeactivateDevice(); + + cur_entry.is_in_focus = true; + cur_entry.is_connected = false; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, + sizeof(hidbus_status)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enable; + INSERT_PADDING_BYTES_NOINIT(7); + BusHandle bus_handle; + u64 inval; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, + "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", + parameters.enable, parameters.bus_handle.abstracted_pad_id, + parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, + parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, + parameters.applet_resource_user_id); + + const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->Enable(parameters.enable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + u32 device_id = device->GetDeviceId(); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(device_id); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto data = ctx.ReadBuffer(); + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_DEBUG(Service_HID, + "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}", + data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type, + bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->SetCommand(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_DEBUG(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + const std::vector<u8> data = device->GetReply(); + const u64 data_size = ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(data_size); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device->GetSendCommandAsycEvent()); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem()); +} + +void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto t_mem_size{rp.Pop<u32>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto polling_mode_{rp.PopEnum<JoyPollingMode>()}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size"); + + LOG_INFO(Service_HID, + "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, " + "internal_index={}, player_number={}, is_valid={}", + t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type, + bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->SetPollingMode(polling_mode_); + device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress())); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->DisablePollingMode(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto manager_type{rp.PopEnum<StatusManagerType>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h new file mode 100644 index 000000000..b10d5156a --- /dev/null +++ b/src/core/hle/service/hid/hidbus.h @@ -0,0 +1,131 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> + +#include "core/hle/service/hid/hidbus/hidbus_base.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Core::Timing { +struct EventType; +} // namespace Core::Timing + +namespace Core { +class System; +} // namespace Core + +namespace Service::HID { + +class HidBus final : public ServiceFramework<HidBus> { +public: + explicit HidBus(Core::System& system_); + ~HidBus() override; + +private: + static const std::size_t max_number_of_handles = 0x13; + + enum class HidBusDeviceId : std::size_t { + RingController = 0x20, + FamicomRight = 0x21, + Starlink = 0x28, + }; + + // This is nn::hidbus::detail::StatusManagerType + enum class StatusManagerType : u32 { + None, + Type16, + Type32, + }; + + // This is nn::hidbus::BusType + enum class BusType : u8 { + LeftJoyRail, + RightJoyRail, + InternalBus, // Lark microphone + + MaxBusType, + }; + + // This is nn::hidbus::BusHandle + struct BusHandle { + u32 abstracted_pad_id; + u8 internal_index; + u8 player_number; + BusType bus_type; + bool is_valid; + }; + static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size"); + + // This is nn::hidbus::JoyPollingReceivedData + struct JoyPollingReceivedData { + std::array<u8, 0x30> data; + u64 out_size; + u64 sampling_number; + }; + static_assert(sizeof(JoyPollingReceivedData) == 0x40, + "JoyPollingReceivedData is an invalid size"); + + struct HidbusStatusManagerEntry { + u8 is_connected{}; + INSERT_PADDING_BYTES(0x3); + ResultCode is_connected_result{0}; + u8 is_enabled{}; + u8 is_in_focus{}; + u8 is_polling_mode{}; + u8 reserved{}; + JoyPollingMode polling_mode{}; + INSERT_PADDING_BYTES(0x70); // Unknown + }; + static_assert(sizeof(HidbusStatusManagerEntry) == 0x80, + "HidbusStatusManagerEntry is an invalid size"); + + struct HidbusStatusManager { + std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{}; + INSERT_PADDING_BYTES(0x680); // Unused + }; + static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size"); + + struct HidbusDevice { + bool is_device_initializated{}; + BusHandle handle{}; + std::unique_ptr<HidbusBase> device{nullptr}; + }; + + void GetBusHandle(Kernel::HLERequestContext& ctx); + void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx); + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void EnableExternalDevice(Kernel::HLERequestContext& ctx); + void GetExternalDeviceId(Kernel::HLERequestContext& ctx); + void SendCommandAsync(Kernel::HLERequestContext& ctx); + void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx); + void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx); + void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); + void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx); + void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx); + void SetStatusManagerType(Kernel::HLERequestContext& ctx); + + void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; + + template <typename T> + void MakeDevice(BusHandle handle) { + const auto device_index = GetDeviceIndexFromHandle(handle); + if (device_index) { + devices[device_index.value()].device = + std::make_unique<T>(system.HIDCore(), service_context); + } + } + + bool is_hidbus_enabled{false}; + HidbusStatusManager hidbus_status{}; + std::array<HidbusDevice, max_number_of_handles> devices{}; + std::shared_ptr<Core::Timing::EventType> hidbus_update_event; + KernelHelpers::ServiceContext service_context; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp new file mode 100644 index 000000000..09bff10e5 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp @@ -0,0 +1,72 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::HID { + +HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_) + : service_context(service_context_) { + send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); +} +HidbusBase::~HidbusBase() = default; + +void HidbusBase::ActivateDevice() { + if (is_activated) { + return; + } + is_activated = true; + OnInit(); +} + +void HidbusBase::DeactivateDevice() { + if (is_activated) { + OnRelease(); + } + is_activated = false; +} + +bool HidbusBase::IsDeviceActivated() const { + return is_activated; +} + +void HidbusBase::Enable(bool enable) { + device_enabled = enable; +} + +bool HidbusBase::IsEnabled() const { + return device_enabled; +} + +bool HidbusBase::IsPollingMode() const { + return polling_mode_enabled; +} + +JoyPollingMode HidbusBase::GetPollingMode() const { + return polling_mode; +} + +void HidbusBase::SetPollingMode(JoyPollingMode mode) { + polling_mode = mode; + polling_mode_enabled = true; +} + +void HidbusBase::DisablePollingMode() { + polling_mode_enabled = false; +} + +void HidbusBase::SetTransferMemoryPointer(u8* t_mem) { + is_transfer_memory_set = true; + transfer_memory = t_mem; +} + +Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const { + return send_command_async_event->GetReadableEvent(); +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h new file mode 100644 index 000000000..13d073a3d --- /dev/null +++ b/src/core/hle/service/hid/hidbus/hidbus_base.h @@ -0,0 +1,179 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} + +namespace Service::HID { + +// This is nn::hidbus::JoyPollingMode +enum class JoyPollingMode : u32 { + SixAxisSensorDisable, + SixAxisSensorEnable, + ButtonOnly, +}; + +struct DataAccessorHeader { + ResultCode result{ResultUnknown}; + INSERT_PADDING_WORDS(0x1); + std::array<u8, 0x18> unused{}; + u64 latest_entry{}; + u64 total_entries{}; +}; +static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size"); + +struct JoyDisableSixAxisPollingData { + std::array<u8, 0x26> data; + u8 out_size; + INSERT_PADDING_BYTES(0x1); + u64 sampling_number; +}; +static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30, + "JoyDisableSixAxisPollingData is an invalid size"); + +struct JoyEnableSixAxisPollingData { + std::array<u8, 0x8> data; + u8 out_size; + INSERT_PADDING_BYTES(0x7); + u64 sampling_number; +}; +static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18, + "JoyEnableSixAxisPollingData is an invalid size"); + +struct JoyButtonOnlyPollingData { + std::array<u8, 0x2c> data; + u8 out_size; + INSERT_PADDING_BYTES(0x3); + u64 sampling_number; +}; +static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38, + "JoyButtonOnlyPollingData is an invalid size"); + +struct JoyDisableSixAxisPollingEntry { + u64 sampling_number; + JoyDisableSixAxisPollingData polling_data; +}; +static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38, + "JoyDisableSixAxisPollingEntry is an invalid size"); + +struct JoyEnableSixAxisPollingEntry { + u64 sampling_number; + JoyEnableSixAxisPollingData polling_data; +}; +static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20, + "JoyEnableSixAxisPollingEntry is an invalid size"); + +struct JoyButtonOnlyPollingEntry { + u64 sampling_number; + JoyButtonOnlyPollingData polling_data; +}; +static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40, + "JoyButtonOnlyPollingEntry is an invalid size"); + +struct JoyDisableSixAxisDataAccessor { + DataAccessorHeader header{}; + std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{}; +}; +static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298, + "JoyDisableSixAxisDataAccessor is an invalid size"); + +struct JoyEnableSixAxisDataAccessor { + DataAccessorHeader header{}; + std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{}; +}; +static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190, + "JoyEnableSixAxisDataAccessor is an invalid size"); + +struct ButtonOnlyPollingDataAccessor { + DataAccessorHeader header; + std::array<JoyButtonOnlyPollingEntry, 0xb> entries; +}; +static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0, + "ButtonOnlyPollingDataAccessor is an invalid size"); + +class HidbusBase { +public: + explicit HidbusBase(KernelHelpers::ServiceContext& service_context_); + virtual ~HidbusBase(); + + void ActivateDevice(); + + void DeactivateDevice(); + + bool IsDeviceActivated() const; + + // Enables/disables the device + void Enable(bool enable); + + // returns true if device is enabled + bool IsEnabled() const; + + // returns true if polling mode is enabled + bool IsPollingMode() const; + + // returns polling mode + JoyPollingMode GetPollingMode() const; + + // Sets and enables JoyPollingMode + void SetPollingMode(JoyPollingMode mode); + + // Disables JoyPollingMode + void DisablePollingMode(); + + // Called on EnableJoyPollingReceiveMode + void SetTransferMemoryPointer(u8* t_mem); + + Kernel::KReadableEvent& GetSendCommandAsycEvent() const; + + virtual void OnInit() {} + + virtual void OnRelease() {} + + // Updates device transfer memory + virtual void OnUpdate() {} + + // Returns the device ID of the joycon + virtual u8 GetDeviceId() const { + return {}; + } + + // Assigns a command from data + virtual bool SetCommand(const std::vector<u8>& data) { + return {}; + } + + // Returns a reply from a command + virtual std::vector<u8> GetReply() const { + return {}; + } + +protected: + bool is_activated{}; + bool device_enabled{}; + bool polling_mode_enabled{}; + JoyPollingMode polling_mode = {}; + // TODO(German77): All data accessors need to be replaced with a ring lifo object + JoyDisableSixAxisDataAccessor disable_sixaxis_data{}; + JoyEnableSixAxisDataAccessor enable_sixaxis_data{}; + ButtonOnlyPollingDataAccessor button_only_data{}; + + u8* transfer_memory{nullptr}; + bool is_transfer_memory_set{}; + + Kernel::KEvent* send_command_async_event; + KernelHelpers::ServiceContext& service_context; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp new file mode 100644 index 000000000..5ec3cc83c --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.cpp @@ -0,0 +1,286 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hid/emulated_devices.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/hidbus/ringcon.h" + +namespace Service::HID { + +RingController::RingController(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) { + input = hid_core_.GetEmulatedDevices(); +} + +RingController::~RingController() = default; + +void RingController::OnInit() { + return; +} + +void RingController::OnRelease() { + return; +}; + +void RingController::OnUpdate() { + if (!is_activated) { + return; + } + + if (!device_enabled) { + return; + } + + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + // TODO: Increment multitasking counters from motion and sensor data + + switch (polling_mode) { + case JoyPollingMode::SixAxisSensorEnable: { + enable_sixaxis_data.header.total_entries = 10; + enable_sixaxis_data.header.result = ResultSuccess; + const auto& last_entry = + enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; + + enable_sixaxis_data.header.latest_entry = + (enable_sixaxis_data.header.latest_entry + 1) % 10; + auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; + + curr_entry.sampling_number = last_entry.sampling_number + 1; + curr_entry.polling_data.sampling_number = curr_entry.sampling_number; + + const RingConData ringcon_value = GetSensorValue(); + curr_entry.polling_data.out_size = sizeof(ringcon_value); + std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value)); + + std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data)); + break; + } + default: + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); + break; + } +} + +RingController::RingConData RingController::GetSensorValue() const { + RingConData ringcon_sensor_value{ + .status = DataValid::Valid, + .data = 0, + }; + + const f32 force_value = input->GetRingSensorForce().force * range; + ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value; + + return ringcon_sensor_value; +} + +u8 RingController::GetDeviceId() const { + return device_id; +} + +std::vector<u8> RingController::GetReply() const { + const RingConCommands current_command = command; + + switch (current_command) { + case RingConCommands::GetFirmwareVersion: + return GetFirmwareVersionReply(); + case RingConCommands::ReadId: + return GetReadIdReply(); + case RingConCommands::c20105: + return GetC020105Reply(); + case RingConCommands::ReadUnkCal: + return GetReadUnkCalReply(); + case RingConCommands::ReadFactoryCal: + return GetReadFactoryCalReply(); + case RingConCommands::ReadUserCal: + return GetReadUserCalReply(); + case RingConCommands::ReadRepCount: + return GetReadRepCountReply(); + case RingConCommands::ReadTotalPushCount: + return GetReadTotalPushCountReply(); + case RingConCommands::ResetRepCount: + return GetResetRepCountReply(); + case RingConCommands::SaveCalData: + return GetSaveDataReply(); + default: + return GetErrorReply(); + } +} + +bool RingController::SetCommand(const std::vector<u8>& data) { + if (data.size() < 4) { + LOG_ERROR(Service_HID, "Command size not supported {}", data.size()); + command = RingConCommands::Error; + return false; + } + + std::memcpy(&command, data.data(), sizeof(RingConCommands)); + + switch (command) { + case RingConCommands::GetFirmwareVersion: + case RingConCommands::ReadId: + case RingConCommands::c20105: + case RingConCommands::ReadUnkCal: + case RingConCommands::ReadFactoryCal: + case RingConCommands::ReadUserCal: + case RingConCommands::ReadRepCount: + case RingConCommands::ReadTotalPushCount: + ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); + send_command_async_event->GetWritableEvent().Signal(); + return true; + case RingConCommands::ResetRepCount: + ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); + total_rep_count = 0; + send_command_async_event->GetWritableEvent().Signal(); + return true; + case RingConCommands::SaveCalData: { + ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); + + SaveCalData save_info{}; + std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); + user_calibration = save_info.calibration; + send_command_async_event->GetWritableEvent().Signal(); + return true; + } + default: + LOG_ERROR(Service_HID, "Command not implemented {}", command); + command = RingConCommands::Error; + // Signal a reply to avoid softlocking the game + send_command_async_event->GetWritableEvent().Signal(); + return false; + } +} + +std::vector<u8> RingController::GetFirmwareVersionReply() const { + const FirmwareVersionReply reply{ + .status = DataValid::Valid, + .firmware = version, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadIdReply() const { + // The values are hardcoded from a real joycon + const ReadIdReply reply{ + .status = DataValid::Valid, + .id_l_x0 = 8, + .id_l_x0_2 = 41, + .id_l_x4 = 22294, + .id_h_x0 = 19777, + .id_h_x0_2 = 13621, + .id_h_x4 = 8245, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetC020105Reply() const { + const Cmd020105Reply reply{ + .status = DataValid::Valid, + .data = 1, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadUnkCalReply() const { + const ReadUnkCalReply reply{ + .status = DataValid::Valid, + .data = 0, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadFactoryCalReply() const { + const ReadFactoryCalReply reply{ + .status = DataValid::Valid, + .calibration = factory_calibration, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadUserCalReply() const { + const ReadUserCalReply reply{ + .status = DataValid::Valid, + .calibration = user_calibration, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadRepCountReply() const { + const GetThreeByteReply reply{ + .status = DataValid::Valid, + .data = {total_rep_count, 0, 0}, + .crc = GetCrcValue({total_rep_count, 0, 0, 0}), + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadTotalPushCountReply() const { + const GetThreeByteReply reply{ + .status = DataValid::Valid, + .data = {total_push_count, 0, 0}, + .crc = GetCrcValue({total_push_count, 0, 0, 0}), + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetResetRepCountReply() const { + return GetReadRepCountReply(); +} + +std::vector<u8> RingController::GetSaveDataReply() const { + const StatusReply reply{ + .status = DataValid::Valid, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetErrorReply() const { + const ErrorReply reply{ + .status = DataValid::BadCRC, + }; + + return GetDataVector(reply); +} + +u8 RingController::GetCrcValue(const std::vector<u8>& data) const { + u8 crc = 0; + for (std::size_t index = 0; index < data.size(); index++) { + for (u8 i = 0x80; i > 0; i >>= 1) { + bool bit = (crc & 0x80) != 0; + if ((data[index] & i) != 0) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x8d; + } + } + } + return crc; +} + +template <typename T> +std::vector<u8> RingController::GetDataVector(const T& reply) const { + static_assert(std::is_trivially_copyable_v<T>); + std::vector<u8> data; + data.resize(sizeof(reply)); + std::memcpy(data.data(), &reply, sizeof(reply)); + return data; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h new file mode 100644 index 000000000..2dbc6150e --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.h @@ -0,0 +1,254 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedDevices; +} // namespace Core::HID + +namespace Service::HID { + +class RingController final : public HidbusBase { +public: + explicit RingController(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~RingController() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; + +private: + // These values are obtained from a real ring controller + static constexpr s16 idle_value = 2280; + static constexpr s16 idle_deadzone = 120; + static constexpr s16 range = 2500; + + // Most missing command names are leftovers from other firmware versions + enum class RingConCommands : u32 { + GetFirmwareVersion = 0x00020000, + ReadId = 0x00020100, + JoyPolling = 0x00020101, + Unknown1 = 0x00020104, + c20105 = 0x00020105, + Unknown2 = 0x00020204, + Unknown3 = 0x00020304, + Unknown4 = 0x00020404, + ReadUnkCal = 0x00020504, + ReadFactoryCal = 0x00020A04, + Unknown5 = 0x00021104, + Unknown6 = 0x00021204, + Unknown7 = 0x00021304, + ReadUserCal = 0x00021A04, + ReadRepCount = 0x00023104, + ReadTotalPushCount = 0x00023204, + ResetRepCount = 0x04013104, + Unknown8 = 0x04011104, + Unknown9 = 0x04011204, + Unknown10 = 0x04011304, + SaveCalData = 0x10011A04, + Error = 0xFFFFFFFF, + }; + + enum class DataValid : u32 { + Valid, + BadCRC, + Cal, + }; + + struct FirmwareVersion { + u8 sub; + u8 main; + }; + static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size"); + + struct FactoryCalibration { + s32_le os_max; + s32_le hk_max; + s32_le zero_min; + s32_le zero_max; + }; + static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size"); + + struct CalibrationValue { + s16 value; + u16 crc; + }; + static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size"); + + struct UserCalibration { + CalibrationValue os_max; + CalibrationValue hk_max; + CalibrationValue zero; + }; + static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size"); + + struct SaveCalData { + RingConCommands command; + UserCalibration calibration; + INSERT_PADDING_BYTES_NOINIT(4); + }; + static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size"); + static_assert(std::is_trivially_copyable_v<SaveCalData>, + "SaveCalData must be trivially copyable"); + + struct FirmwareVersionReply { + DataValid status; + FirmwareVersion firmware; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size"); + + struct Cmd020105Reply { + DataValid status; + u8 data; + INSERT_PADDING_BYTES(0x3); + }; + static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size"); + + struct StatusReply { + DataValid status; + }; + static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size"); + + struct GetThreeByteReply { + DataValid status; + std::array<u8, 3> data; + u8 crc; + }; + static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size"); + + struct ReadUnkCalReply { + DataValid status; + u16 data; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size"); + + struct ReadFactoryCalReply { + DataValid status; + FactoryCalibration calibration; + }; + static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size"); + + struct ReadUserCalReply { + DataValid status; + UserCalibration calibration; + INSERT_PADDING_BYTES(0x4); + }; + static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size"); + + struct ReadIdReply { + DataValid status; + u16 id_l_x0; + u16 id_l_x0_2; + u16 id_l_x4; + u16 id_h_x0; + u16 id_h_x0_2; + u16 id_h_x4; + }; + static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size"); + + struct ErrorReply { + DataValid status; + INSERT_PADDING_BYTES(0x3); + }; + static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size"); + + struct RingConData { + DataValid status; + s16_le data; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size"); + + // Returns RingConData struct with pressure sensor values + RingConData GetSensorValue() const; + + // Returns 8 byte reply with firmware version + std::vector<u8> GetFirmwareVersionReply() const; + + // Returns 16 byte reply with ID values + std::vector<u8> GetReadIdReply() const; + + // (STUBBED) Returns 8 byte reply + std::vector<u8> GetC020105Reply() const; + + // (STUBBED) Returns 8 byte empty reply + std::vector<u8> GetReadUnkCalReply() const; + + // Returns 20 byte reply with factory calibration values + std::vector<u8> GetReadFactoryCalReply() const; + + // Returns 20 byte reply with user calibration values + std::vector<u8> GetReadUserCalReply() const; + + // Returns 8 byte reply + std::vector<u8> GetReadRepCountReply() const; + + // Returns 8 byte reply + std::vector<u8> GetReadTotalPushCountReply() const; + + // Returns 8 byte reply + std::vector<u8> GetResetRepCountReply() const; + + // Returns 4 byte save data reply + std::vector<u8> GetSaveDataReply() const; + + // Returns 8 byte error reply + std::vector<u8> GetErrorReply() const; + + // Returns 8 bit redundancy check from provided data + u8 GetCrcValue(const std::vector<u8>& data) const; + + // Converts structs to an u8 vector equivalent + template <typename T> + std::vector<u8> GetDataVector(const T& reply) const; + + RingConCommands command{RingConCommands::Error}; + + // These counters are used in multitasking mode while the switch is sleeping + // Total steps taken + u8 total_rep_count = 0; + // Total times the ring was pushed + u8 total_push_count = 0; + + const u8 device_id = 0x20; + const FirmwareVersion version = { + .sub = 0x0, + .main = 0x2c, + }; + const FactoryCalibration factory_calibration = { + .os_max = idle_value + range + idle_deadzone, + .hk_max = idle_value - range - idle_deadzone, + .zero_min = idle_value - idle_deadzone, + .zero_max = idle_value + idle_deadzone, + }; + UserCalibration user_calibration = { + .os_max = {.value = range, .crc = 228}, + .hk_max = {.value = -range, .crc = 239}, + .zero = {.value = idle_value, .crc = 225}, + }; + + Core::HID::EmulatedDevices* input; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp new file mode 100644 index 000000000..3175c48da --- /dev/null +++ b/src/core/hle/service/hid/hidbus/starlink.cpp @@ -0,0 +1,51 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/hidbus/starlink.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0x28; + +Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) {} +Starlink::~Starlink() = default; + +void Starlink::OnInit() { + return; +} + +void Starlink::OnRelease() { + return; +}; + +void Starlink::OnUpdate() { + if (!is_activated) { + return; + } + if (!device_enabled) { + return; + } + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); +} + +u8 Starlink::GetDeviceId() const { + return DEVICE_ID; +} + +std::vector<u8> Starlink::GetReply() const { + return {}; +} + +bool Starlink::SetCommand(const std::vector<u8>& data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h new file mode 100644 index 000000000..79770b68e --- /dev/null +++ b/src/core/hle/service/hid/hidbus/starlink.h @@ -0,0 +1,39 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class Starlink final : public HidbusBase { +public: + explicit Starlink(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~Starlink() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp new file mode 100644 index 000000000..5474657be --- /dev/null +++ b/src/core/hle/service/hid/hidbus/stubbed.cpp @@ -0,0 +1,52 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/hidbus/stubbed.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0xFF; + +HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) {} +HidbusStubbed::~HidbusStubbed() = default; + +void HidbusStubbed::OnInit() { + return; +} + +void HidbusStubbed::OnRelease() { + return; +}; + +void HidbusStubbed::OnUpdate() { + if (!is_activated) { + return; + } + if (!device_enabled) { + return; + } + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); +} + +u8 HidbusStubbed::GetDeviceId() const { + return DEVICE_ID; +} + +std::vector<u8> HidbusStubbed::GetReply() const { + return {}; +} + +bool HidbusStubbed::SetCommand(const std::vector<u8>& data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h new file mode 100644 index 000000000..40acdfe8f --- /dev/null +++ b/src/core/hle/service/hid/hidbus/stubbed.h @@ -0,0 +1,39 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class HidbusStubbed final : public HidbusBase { +public: + explicit HidbusStubbed(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~HidbusStubbed() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID |