diff options
| author | german77 <juangerman-13@hotmail.com> | 2021-05-02 18:41:03 -0500 | 
|---|---|---|
| committer | Narr the Reg <juangerman-13@hotmail.com> | 2022-04-16 00:49:21 -0500 | 
| commit | b2359f1527df30e151a44221f4276e6b14ec0bca (patch) | |
| tree | e8b887327b39ad3a07dde8d7d9c85a55e4ff7494 | |
| parent | 856a841c72ba7b6c7f63a3afafd1692c1cea8744 (diff) | |
hidbus: Implement hidbus and ringcon
| -rw-r--r-- | src/core/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 18 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/hid/hid.cpp | 27 | ||||
| -rw-r--r-- | src/core/hle/service/hid/hidbus.cpp | 529 | ||||
| -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 | 178 | ||||
| -rw-r--r-- | src/core/hle/service/hid/hidbus/ringcon.cpp | 306 | ||||
| -rw-r--r-- | src/core/hle/service/hid/hidbus/ringcon.h | 247 | ||||
| -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 | 
14 files changed, 1679 insertions, 26 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b681d21a7..62230bae0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -434,6 +434,8 @@ add_library(core STATIC      hle/service/grc/grc.h      hle/service/hid/hid.cpp      hle/service/hid/hid.h +    hle/service/hid/hidbus.cpp +    hle/service/hid/hidbus.h      hle/service/hid/irs.cpp      hle/service/hid/irs.h      hle/service/hid/ring_lifo.h @@ -460,6 +462,14 @@ add_library(core STATIC      hle/service/hid/controllers/touchscreen.h      hle/service/hid/controllers/xpad.cpp      hle/service/hid/controllers/xpad.h +    hle/service/hid/hidbus/hidbus_base.cpp +    hle/service/hid/hidbus/hidbus_base.h +    hle/service/hid/hidbus/ringcon.cpp +    hle/service/hid/hidbus/ringcon.h +    hle/service/hid/hidbus/starlink.cpp +    hle/service/hid/hidbus/starlink.h +    hle/service/hid/hidbus/stubbed.cpp +    hle/service/hid/hidbus/stubbed.h      hle/service/jit/jit_context.cpp      hle/service/jit/jit_context.h      hle/service/jit/jit.cpp diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d840d44e6..5984afd7e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -140,6 +140,7 @@ struct KernelCore::Impl {          CleanupObject(font_shared_mem);          CleanupObject(irs_shared_mem);          CleanupObject(time_shared_mem); +        CleanupObject(hidbus_shared_mem);          CleanupObject(system_resource_limit);          for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { @@ -622,16 +623,20 @@ struct KernelCore::Impl {          constexpr std::size_t font_size{0x1100000};          constexpr std::size_t irs_size{0x8000};          constexpr std::size_t time_size{0x1000}; +        constexpr std::size_t hidbus_size{0x1000};          const PAddr hid_phys_addr{system_pool.GetAddress()};          const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};          const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};          const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; +        const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size + +                                     time_size};          hid_shared_mem = KSharedMemory::Create(system.Kernel());          font_shared_mem = KSharedMemory::Create(system.Kernel());          irs_shared_mem = KSharedMemory::Create(system.Kernel());          time_shared_mem = KSharedMemory::Create(system.Kernel()); +        hidbus_shared_mem = KSharedMemory::Create(system.Kernel());          hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,                                     {hid_phys_addr, hid_size / PageSize}, @@ -649,6 +654,10 @@ struct KernelCore::Impl {                                      {time_phys_addr, time_size / PageSize},                                      Svc::MemoryPermission::None, Svc::MemoryPermission::Read,                                      time_phys_addr, time_size, "Time:SharedMemory"); +        hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, +                                      {hidbus_phys_addr, hidbus_size / PageSize}, +                                      Svc::MemoryPermission::None, Svc::MemoryPermission::Read, +                                      hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");      }      KClientPort* CreateNamedServicePort(std::string name) { @@ -748,6 +757,7 @@ struct KernelCore::Impl {      Kernel::KSharedMemory* font_shared_mem{};      Kernel::KSharedMemory* irs_shared_mem{};      Kernel::KSharedMemory* time_shared_mem{}; +    Kernel::KSharedMemory* hidbus_shared_mem{};      // Memory layout      std::unique_ptr<KMemoryLayout> memory_layout; @@ -1047,6 +1057,14 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {      return *impl->time_shared_mem;  } +Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() { +    return *impl->hidbus_shared_mem; +} + +const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const { +    return *impl->hidbus_shared_mem; +} +  void KernelCore::Suspend(bool in_suspention) {      const bool should_suspend = exception_exited || in_suspention;      { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index d709c368b..12e44b8a5 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -264,6 +264,12 @@ public:      /// Gets the shared memory object for Time services.      const Kernel::KSharedMemory& GetTimeSharedMem() const; +    /// Gets the shared memory object for HIDBus services. +    Kernel::KSharedMemory& GetHidBusSharedMem(); + +    /// Gets the shared memory object for HIDBus services. +    const Kernel::KSharedMemory& GetHidBusSharedMem() const; +      /// Suspend/unsuspend the OS.      void Suspend(bool in_suspention); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b2cec2253..9d3e0a658 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" @@ -2128,32 +2129,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/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp new file mode 100644 index 000000000..db2864277 --- /dev/null +++ b/src/core/hle/service/hid/hidbus.cpp @@ -0,0 +1,529 @@ +// 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 "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); +    } + +    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) { +            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..9cac0be80 --- /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_asyc_event = service_context.CreateEvent("hidbus:SendCommandAsycEvent"); +} +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_asyc_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..41e571998 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/hidbus_base.h @@ -0,0 +1,178 @@ +// 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 = {}; +    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_asyc_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..6fe68081f --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.cpp @@ -0,0 +1,306 @@ +// 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/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_) { +    // Use the horizontal axis of left stick for emulating input +    // There is no point on adding a frontend implementation since Ring Fit Adventure doesn't work +    input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1); +} + +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; +    } + +    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 stick_value = static_cast<f32>(input->GetSticks().left.x) / 32767.0f; + +    ringcon_sensor_value.data = static_cast<s16>(stick_value * range) + 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::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; +    } + +    // There must be a better way to do this +    const u32 command_id = +        u32{data[0]} + (u32{data[1]} << 8) + (u32{data[2]} << 16) + (u32{data[3]} << 24); +    static constexpr std::array supported_commands = { +        RingConCommands::GetFirmwareVersion, +        RingConCommands::ReadId, +        RingConCommands::c20105, +        RingConCommands::ReadUnkCal, +        RingConCommands::ReadFactoryCal, +        RingConCommands::ReadUserCal, +        RingConCommands::ReadRepCount, +        RingConCommands::ReadTotalPushCount, +        RingConCommands::SaveCalData, +    }; + +    for (RingConCommands cmd : supported_commands) { +        if (command_id == static_cast<u32>(cmd)) { +            return ExcecuteCommand(cmd, data); +        } +    } + +    LOG_ERROR(Service_HID, "Command not implemented {}", command_id); +    command = RingConCommands::Error; +    // Signal a reply to avoid softlocking +    send_command_asyc_event->GetWritableEvent().Signal(); +    return false; +} + +bool RingController::ExcecuteCommand(RingConCommands cmd, const std::vector<u8>& data) { +    switch (cmd) { +    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"); +        command = cmd; +        send_command_asyc_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, sizeof(SaveCalData)); +        user_calibration = save_info.calibration; + +        command = cmd; +        send_command_asyc_event->GetWritableEvent().Signal(); +        return true; +    } +    default: +        LOG_ERROR(Service_HID, "Command not implemented {}", cmd); +        command = RingConCommands::Error; +        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 { +    // The values are hardcoded from a real joycon +    const GetThreeByteReply reply{ +        .status = DataValid::Valid, +        .data = {30, 0, 0}, +        .crc = GetCrcValue({30, 0, 0, 0}), +    }; + +    return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadTotalPushCountReply() const { +    // The values are hardcoded from a real joycon +    const GetThreeByteReply reply{ +        .status = DataValid::Valid, +        .data = {30, 0, 0}, +        .crc = GetCrcValue({30, 0, 0, 0}), +    }; + +    return GetDataVector(reply); +} + +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..e8b3d8254 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.h @@ -0,0 +1,247 @@ +// 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 EmulatedController; +} // 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; + +    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, +        Unknown9 = 0x04013104, +        Unknown10 = 0x04011104, +        Unknown11 = 0x04011204, +        Unknown12 = 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"); + +    // Executes the command requested +    bool ExcecuteCommand(RingConCommands cmd, const std::vector<u8>& data); + +    // 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; + +    // (STUBBED) Returns 8 byte reply +    std::vector<u8> GetReadRepCountReply() const; + +    // (STUBBED) Returns 8 byte reply +    std::vector<u8> GetReadTotalPushCountReply() 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}; + +    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/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  | 
