yuzu/src/core/hle/service/hid/hidbus.cpp
Morph 99ceb03a1c general: Convert source file copyright comments over to SPDX
This formats all copyright comments according to SPDX formatting guidelines.
Additionally, this resolves the remaining GPLv2 only licensed files by relicensing them to GPLv2.0-or-later.
2022-04-23 05:55:32 -04:00

531 lines
19 KiB
C++

// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hid/hid_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/hid/hidbus/ringcon.h"
#include "core/hle/service/hid/hidbus/starlink.h"
#include "core/hle/service/hid/hidbus/stubbed.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::HID {
// (15ms, 66Hz)
constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
HidBus::HidBus(Core::System& system_)
: ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &HidBus::GetBusHandle, "GetBusHandle"},
{2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
{3, &HidBus::Initialize, "Initialize"},
{4, &HidBus::Finalize, "Finalize"},
{5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
{6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
{7, &HidBus::SendCommandAsync, "SendCommandAsync"},
{8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
{9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
{10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
{11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
{12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
{13, nullptr, "GetPollingData"},
{14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
};
// clang-format on
RegisterHandlers(functions);
// Register update callbacks
hidbus_update_event = Core::Timing::CreateEvent(
"Hidbus::UpdateCallback",
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
const auto guard = LockService();
UpdateHidbus(user_data, ns_late);
});
system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
}
HidBus::~HidBus() {
system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
}
void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initializated) {
continue;
}
auto& device = devices[i].device;
device->OnUpdate();
auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
cur_entry.is_polling_mode = device->IsPollingMode();
cur_entry.polling_mode = device->GetPollingMode();
cur_entry.is_enabled = device->IsEnabled();
u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
sizeof(HidbusStatusManagerEntry));
}
}
// If ns_late is higher than the update rate ignore the delay
if (ns_late > hidbus_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
}
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
for (std::size_t i = 0; i < devices.size(); ++i) {
const auto& device_handle = devices[i].handle;
if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
handle.internal_index == device_handle.internal_index &&
handle.player_number == device_handle.player_number &&
handle.bus_type == device_handle.bus_type &&
handle.is_valid == device_handle.is_valid) {
return i;
}
}
return std::nullopt;
}
void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
BusType bus_type;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
bool is_handle_found = 0;
std::size_t handle_index = 0;
for (std::size_t i = 0; i < devices.size(); i++) {
const auto& handle = devices[i].handle;
if (!handle.is_valid) {
continue;
}
if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
handle.bus_type == parameters.bus_type) {
is_handle_found = true;
handle_index = i;
break;
}
}
// Handle not found. Create a new one
if (!is_handle_found) {
for (std::size_t i = 0; i < devices.size(); i++) {
if (devices[i].handle.is_valid) {
continue;
}
devices[i].handle = {
.abstracted_pad_id = static_cast<u8>(i),
.internal_index = static_cast<u8>(i),
.player_number = static_cast<u8>(parameters.npad_id),
.bus_type = parameters.bus_type,
.is_valid = true,
};
handle_index = i;
break;
}
}
struct OutData {
bool is_valid;
INSERT_PADDING_BYTES(7);
BusHandle handle;
};
static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
const OutData out_data{
.is_valid = true,
.handle = devices[handle_index].handle,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(out_data);
}
void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID,
"Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
const bool is_attached = device->IsDeviceActivated();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_attached);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={} bus_type={} internal_index={} "
"player_number={} is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
is_hidbus_enabled = true;
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
MakeDevice<RingController>(bus_handle_);
devices[device_index.value()].is_device_initializated = true;
devices[device_index.value()].device->ActivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = true;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
} else {
MakeDevice<HidbusStubbed>(bus_handle_);
devices[device_index.value()].is_device_initializated = true;
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
}
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
auto& device = devices[device_index.value()].device;
devices[device_index.value()].is_device_initializated = false;
device->DeactivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool enable;
INSERT_PADDING_BYTES_NOINIT(7);
BusHandle bus_handle;
u64 inval;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID,
"called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
parameters.enable, parameters.bus_handle.abstracted_pad_id,
parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
parameters.applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->Enable(parameters.enable);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
u32 device_id = device->GetDeviceId();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(device_id);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto data = ctx.ReadBuffer();
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID,
"called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}",
data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->SetCommand(data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
};
void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
const std::vector<u8> data = device->GetReply();
const u64 data_size = ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(data_size);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
};
void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device->GetSendCommandAsycEvent());
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
};
void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
}
void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto t_mem_size{rp.Pop<u32>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
LOG_INFO(Service_HID,
"called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
"internal_index={}, player_number={}, is_valid={}",
t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->SetPollingMode(polling_mode_);
device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->DisablePollingMode();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto manager_type{rp.PopEnum<StatusManagerType>()};
LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
};
} // namespace Service::HID