hidbus: Implement hidbus and ringcon
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										529
									
								
								src/core/hle/service/hid/hidbus.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										529
									
								
								src/core/hle/service/hid/hidbus.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										131
									
								
								src/core/hle/service/hid/hidbus.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/core/hle/service/hid/hidbus.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										72
									
								
								src/core/hle/service/hid/hidbus/hidbus_base.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/core/hle/service/hid/hidbus/hidbus_base.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										178
									
								
								src/core/hle/service/hid/hidbus/hidbus_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								src/core/hle/service/hid/hidbus/hidbus_base.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										306
									
								
								src/core/hle/service/hid/hidbus/ringcon.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								src/core/hle/service/hid/hidbus/ringcon.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										247
									
								
								src/core/hle/service/hid/hidbus/ringcon.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								src/core/hle/service/hid/hidbus/ringcon.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										51
									
								
								src/core/hle/service/hid/hidbus/starlink.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/core/hle/service/hid/hidbus/starlink.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										39
									
								
								src/core/hle/service/hid/hidbus/starlink.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/core/hle/service/hid/hidbus/starlink.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										52
									
								
								src/core/hle/service/hid/hidbus/stubbed.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/core/hle/service/hid/hidbus/stubbed.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										39
									
								
								src/core/hle/service/hid/hidbus/stubbed.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/core/hle/service/hid/hidbus/stubbed.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
		Reference in New Issue
	
	Block a user