diff options
author | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-05 03:37:43 +0100 |
---|---|---|
committer | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-05 18:41:15 +0100 |
commit | ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch) | |
tree | 3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/hid_core/hidbus | |
parent | Merge pull request #12437 from ameerj/gl-amd-fixes (diff) | |
download | yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.gz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.bz2 yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.lz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.xz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.zst yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.zip |
Diffstat (limited to 'src/hid_core/hidbus')
-rw-r--r-- | src/hid_core/hidbus/hidbus_base.cpp | 73 | ||||
-rw-r--r-- | src/hid_core/hidbus/hidbus_base.h | 183 | ||||
-rw-r--r-- | src/hid_core/hidbus/ringcon.cpp | 292 | ||||
-rw-r--r-- | src/hid_core/hidbus/ringcon.h | 253 | ||||
-rw-r--r-- | src/hid_core/hidbus/starlink.cpp | 50 | ||||
-rw-r--r-- | src/hid_core/hidbus/starlink.h | 37 | ||||
-rw-r--r-- | src/hid_core/hidbus/stubbed.cpp | 50 | ||||
-rw-r--r-- | src/hid_core/hidbus/stubbed.h | 37 |
8 files changed, 975 insertions, 0 deletions
diff --git a/src/hid_core/hidbus/hidbus_base.cpp b/src/hid_core/hidbus/hidbus_base.cpp new file mode 100644 index 000000000..632bb173b --- /dev/null +++ b/src/hid_core/hidbus/hidbus_base.cpp @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/kernel_helpers.h" +#include "hid_core/hid_core.h" +#include "hid_core/hidbus/hidbus_base.h" + +namespace Service::HID { + +HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_) + : system(system_), service_context(service_context_) { + send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); +} + +HidbusBase::~HidbusBase() { + service_context.CloseEvent(send_command_async_event); +}; + +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::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { + transfer_memory = t_mem; +} + +Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const { + return send_command_async_event->GetReadableEvent(); +} + +} // namespace Service::HID diff --git a/src/hid_core/hidbus/hidbus_base.h b/src/hid_core/hidbus/hidbus_base.h new file mode 100644 index 000000000..ec41684e1 --- /dev/null +++ b/src/hid_core/hidbus/hidbus_base.h @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <span> +#include "common/typed_address.h" +#include "core/hle/result.h" + +namespace Core { +class System; +} + +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 { + Result 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(Core::System& system_, 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 SetTransferMemoryAddress(Common::ProcessAddress 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(std::span<const 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{}; + + Common::ProcessAddress transfer_memory{}; + + Core::System& system; + Kernel::KEvent* send_command_async_event; + KernelHelpers::ServiceContext& service_context; +}; +} // namespace Service::HID diff --git a/src/hid_core/hidbus/ringcon.cpp b/src/hid_core/hidbus/ringcon.cpp new file mode 100644 index 000000000..cedf25c16 --- /dev/null +++ b/src/hid_core/hidbus/ringcon.cpp @@ -0,0 +1,292 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/memory.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hidbus/ringcon.h" + +namespace Service::HID { + +RingController::RingController(Core::System& system_, + KernelHelpers::ServiceContext& service_context_) + : HidbusBase(system_, service_context_) { + input = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); +} + +RingController::~RingController() = default; + +void RingController::OnInit() { + input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::Ring); + return; +} + +void RingController::OnRelease() { + input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::Active); + return; +}; + +void RingController::OnUpdate() { + if (!is_activated) { + return; + } + + if (!device_enabled) { + return; + } + + if (!polling_mode_enabled || transfer_memory == 0) { + 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)); + + system.ApplicationMemory().WriteBlock(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(std::span<const 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->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->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->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->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/hid_core/hidbus/ringcon.h b/src/hid_core/hidbus/ringcon.h new file mode 100644 index 000000000..0953e8100 --- /dev/null +++ b/src/hid_core/hidbus/ringcon.h @@ -0,0 +1,253 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <span> + +#include "common/common_types.h" +#include "hid_core/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class RingController final : public HidbusBase { +public: + explicit RingController(Core::System& system_, 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(std::span<const 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::EmulatedController* input; +}; +} // namespace Service::HID diff --git a/src/hid_core/hidbus/starlink.cpp b/src/hid_core/hidbus/starlink.cpp new file mode 100644 index 000000000..31b263aa1 --- /dev/null +++ b/src/hid_core/hidbus/starlink.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hidbus/starlink.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0x28; + +Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_) + : HidbusBase(system_, 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 || transfer_memory == 0) { + 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(std::span<const u8> data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/hid_core/hidbus/starlink.h b/src/hid_core/hidbus/starlink.h new file mode 100644 index 000000000..ee37763b4 --- /dev/null +++ b/src/hid_core/hidbus/starlink.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "hid_core/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class Starlink final : public HidbusBase { +public: + explicit Starlink(Core::System& system_, 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(std::span<const u8> data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID diff --git a/src/hid_core/hidbus/stubbed.cpp b/src/hid_core/hidbus/stubbed.cpp new file mode 100644 index 000000000..f16051aa9 --- /dev/null +++ b/src/hid_core/hidbus/stubbed.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hidbus/stubbed.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0xFF; + +HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_) + : HidbusBase(system_, 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 || transfer_memory == 0) { + 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(std::span<const u8> data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/hid_core/hidbus/stubbed.h b/src/hid_core/hidbus/stubbed.h new file mode 100644 index 000000000..7a711cea0 --- /dev/null +++ b/src/hid_core/hidbus/stubbed.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "hid_core/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class HidbusStubbed final : public HidbusBase { +public: + explicit HidbusStubbed(Core::System& system_, 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(std::span<const u8> data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID |