another try

This commit is contained in:
mgthepro
2022-11-05 13:58:44 +01:00
parent 4a9f2bbf2a
commit 9f63fbe700
2002 changed files with 671171 additions and 671092 deletions

View File

@@ -1,73 +1,73 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/console_sixaxis.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
console = hid_core.GetEmulatedConsole();
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
"ConsoleSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
void Controller_ConsoleSixAxis::OnInit() {}
void Controller_ConsoleSixAxis::OnRelease() {}
void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated() || !is_transfer_memory_set) {
seven_sixaxis_lifo.buffer_count = 0;
seven_sixaxis_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
const auto motion_status = console->GetMotion();
last_global_timestamp = core_timing.GetGlobalTimeNs().count();
// This value increments every time the switch goes to sleep
next_seven_sixaxis_state.unknown = 1;
next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
next_seven_sixaxis_state.accel = motion_status.accel;
next_seven_sixaxis_state.gyro = motion_status.gyro;
next_seven_sixaxis_state.quaternion = {
{
motion_status.quaternion.xyz.y,
motion_status.quaternion.xyz.x,
-motion_status.quaternion.w,
},
-motion_status.quaternion.xyz.z,
};
shared_memory->sampling_number++;
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
shared_memory->verticalization_error = motion_status.verticalization_error;
shared_memory->gyro_bias = motion_status.gyro_bias;
// Update seven six axis transfer memory
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo));
}
void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
is_transfer_memory_set = true;
transfer_memory = t_mem;
}
void Controller_ConsoleSixAxis::ResetTimestamp() {
last_saved_timestamp = last_global_timestamp;
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/console_sixaxis.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
console = hid_core.GetEmulatedConsole();
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
"ConsoleSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
void Controller_ConsoleSixAxis::OnInit() {}
void Controller_ConsoleSixAxis::OnRelease() {}
void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated() || !is_transfer_memory_set) {
seven_sixaxis_lifo.buffer_count = 0;
seven_sixaxis_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
const auto motion_status = console->GetMotion();
last_global_timestamp = core_timing.GetGlobalTimeNs().count();
// This value increments every time the switch goes to sleep
next_seven_sixaxis_state.unknown = 1;
next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
next_seven_sixaxis_state.accel = motion_status.accel;
next_seven_sixaxis_state.gyro = motion_status.gyro;
next_seven_sixaxis_state.quaternion = {
{
motion_status.quaternion.xyz.y,
motion_status.quaternion.xyz.x,
-motion_status.quaternion.w,
},
-motion_status.quaternion.xyz.z,
};
shared_memory->sampling_number++;
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
shared_memory->verticalization_error = motion_status.verticalization_error;
shared_memory->gyro_bias = motion_status.gyro_bias;
// Update seven six axis transfer memory
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo));
}
void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
is_transfer_memory_set = true;
transfer_memory = t_mem;
}
void Controller_ConsoleSixAxis::ResetTimestamp() {
last_saved_timestamp = last_global_timestamp;
}
} // namespace Service::HID

View File

@@ -1,73 +1,73 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "common/quaternion.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
class Controller_ConsoleSixAxis final : public ControllerBase {
public:
explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_ConsoleSixAxis() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// Called on InitializeSevenSixAxisSensor
void SetTransferMemoryPointer(u8* t_mem);
// Called on ResetSevenSixAxisSensorTimestamp
void ResetTimestamp();
private:
struct SevenSixAxisState {
INSERT_PADDING_WORDS(2); // unused
u64 timestamp{};
u64 sampling_number{};
u64 unknown{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Quaternion<f32> quaternion{};
};
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSharedMemory {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
SevenSixAxisState next_seven_sixaxis_state{};
u8* transfer_memory = nullptr;
ConsoleSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
bool is_transfer_memory_set = false;
u64 last_saved_timestamp{};
u64 last_global_timestamp{};
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "common/quaternion.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
class Controller_ConsoleSixAxis final : public ControllerBase {
public:
explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_ConsoleSixAxis() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// Called on InitializeSevenSixAxisSensor
void SetTransferMemoryPointer(u8* t_mem);
// Called on ResetSevenSixAxisSensorTimestamp
void ResetTimestamp();
private:
struct SevenSixAxisState {
INSERT_PADDING_WORDS(2); // unused
u64 timestamp{};
u64 sampling_number{};
u64 unknown{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Quaternion<f32> quaternion{};
};
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSharedMemory {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
SevenSixAxisState next_seven_sixaxis_state{};
u8* transfer_memory = nullptr;
ConsoleSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
bool is_transfer_memory_set = false;
u64 last_saved_timestamp{};
u64 last_global_timestamp{};
};
} // namespace Service::HID

View File

@@ -1,29 +1,29 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
ControllerBase::~ControllerBase() = default;
void ControllerBase::ActivateController() {
if (is_activated) {
return;
}
is_activated = true;
OnInit();
}
void ControllerBase::DeactivateController() {
if (is_activated) {
OnRelease();
}
is_activated = false;
}
bool ControllerBase::IsControllerActivated() const {
return is_activated;
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
ControllerBase::~ControllerBase() = default;
void ControllerBase::ActivateController() {
if (is_activated) {
return;
}
is_activated = true;
OnInit();
}
void ControllerBase::DeactivateController() {
if (is_activated) {
OnRelease();
}
is_activated = false;
}
bool ControllerBase::IsControllerActivated() const {
return is_activated;
}
} // namespace Service::HID

View File

@@ -1,48 +1,48 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Core::Timing {
class CoreTiming;
}
namespace Core::HID {
class HIDCore;
}
namespace Service::HID {
class ControllerBase {
public:
explicit ControllerBase(Core::HID::HIDCore& hid_core_);
virtual ~ControllerBase();
// Called when the controller is initialized
virtual void OnInit() = 0;
// When the controller is released
virtual void OnRelease() = 0;
// When the controller is requesting an update for the shared memory
virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0;
// When the controller is requesting a motion update for the shared memory
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
void ActivateController();
void DeactivateController();
bool IsControllerActivated() const;
static const std::size_t hid_entry_count = 17;
static const std::size_t shared_memory_size = 0x40000;
protected:
bool is_activated{false};
Core::HID::HIDCore& hid_core;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Core::Timing {
class CoreTiming;
}
namespace Core::HID {
class HIDCore;
}
namespace Service::HID {
class ControllerBase {
public:
explicit ControllerBase(Core::HID::HIDCore& hid_core_);
virtual ~ControllerBase();
// Called when the controller is initialized
virtual void OnInit() = 0;
// When the controller is released
virtual void OnRelease() = 0;
// When the controller is requesting an update for the shared memory
virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0;
// When the controller is requesting a motion update for the shared memory
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
void ActivateController();
void DeactivateController();
bool IsControllerActivated() const;
static const std::size_t hid_entry_count = 17;
static const std::size_t shared_memory_size = 0x40000;
protected:
bool is_activated{false};
Core::HID::HIDCore& hid_core;
};
} // namespace Service::HID

View File

@@ -1,55 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
"DebugPadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
void Controller_DebugPad::OnRelease() {}
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->debug_pad_lifo.buffer_count = 0;
shared_memory->debug_pad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.debug_pad_enabled) {
next_state.attribute.connected.Assign(1);
const auto& button_state = controller->GetDebugPadButtons();
const auto& stick_state = controller->GetSticks();
next_state.pad_state = button_state;
next_state.l_stick = stick_state.left;
next_state.r_stick = stick_state.right;
}
shared_memory->debug_pad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
"DebugPadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
void Controller_DebugPad::OnRelease() {}
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->debug_pad_lifo.buffer_count = 0;
shared_memory->debug_pad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.debug_pad_enabled) {
next_state.attribute.connected.Assign(1);
const auto& button_state = controller->GetDebugPadButtons();
const auto& stick_state = controller->GetSticks();
next_state.pad_state = button_state;
next_state.l_stick = stick_state.left;
next_state.r_stick = stick_state.right;
}
shared_memory->debug_pad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -1,64 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedController;
struct DebugPadButton;
struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
public:
explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_DebugPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::DebugPadAttribute
struct DebugPadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> connected;
};
};
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
// This is nn::hid::DebugPadState
struct DebugPadState {
s64 sampling_number{};
DebugPadAttribute attribute{};
Core::HID::DebugPadButton pad_state{};
Core::HID::AnalogStickState r_stick{};
Core::HID::AnalogStickState l_stick{};
};
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
struct DebugPadSharedMemory {
// This is nn::hid::detail::DebugPadLifo
Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size");
DebugPadState next_state{};
DebugPadSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedController* controller = nullptr;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedController;
struct DebugPadButton;
struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
public:
explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_DebugPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::DebugPadAttribute
struct DebugPadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> connected;
};
};
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
// This is nn::hid::DebugPadState
struct DebugPadState {
s64 sampling_number{};
DebugPadAttribute attribute{};
Core::HID::DebugPadButton pad_state{};
Core::HID::AnalogStickState r_stick{};
Core::HID::AnalogStickState l_stick{};
};
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
struct DebugPadSharedMemory {
// This is nn::hid::detail::DebugPadLifo
Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size");
DebugPadState next_state{};
DebugPadSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedController* controller = nullptr;
};
} // namespace Service::HID

View File

@@ -1,354 +1,354 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/gesture.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
constexpr f32 angle_threshold = 0.015f; // Threshold in radians
constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
constexpr f32 press_delay = 0.5f; // Time in seconds
constexpr f32 double_tap_delay = 0.35f; // Time in seconds
constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num);
}
Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase(hid_core_) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
"GestureSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
force_update = true;
}
void Controller_Gesture::OnRelease() {}
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
return;
}
ReadTouchInput();
GestureProperties gesture = GetGestureProperties();
f32 time_difference =
static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
(1000 * 1000 * 1000);
// Only update if necesary
if (!ShouldUpdateGesture(gesture, time_difference)) {
return;
}
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
UpdateGestureSharedMemory(gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
const auto touch_status = console->GetTouch();
for (std::size_t id = 0; id < fingers.size(); ++id) {
fingers[id] = touch_status[id];
}
}
bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
if (force_update) {
force_update = false;
return true;
}
// Update if coordinates change
for (size_t id = 0; id < MAX_POINTS; id++) {
if (gesture.points[id] != last_gesture.points[id]) {
return true;
}
}
// Update on press and hold event after 0.5 seconds
if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
time_difference > press_delay) {
return enable_press_and_tap;
}
return false;
}
void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle;
GestureAttribute attributes{};
const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
// Reset next state to default
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.delta = {};
next_state.vel_x = 0;
next_state.vel_y = 0;
next_state.direction = GestureDirection::None;
next_state.rotation_angle = 0;
next_state.scale = 0;
if (gesture.active_points > 0) {
if (last_gesture.active_points == 0) {
NewGesture(gesture, type, attributes);
} else {
UpdateExistingGesture(gesture, type, time_difference);
}
} else {
EndGesture(gesture, last_gesture, type, attributes, time_difference);
}
// Apply attributes
next_state.detection_count = gesture.detection_count;
next_state.type = type;
next_state.attributes = attributes;
next_state.pos = gesture.mid_point;
next_state.point_count = static_cast<s32>(gesture.active_points);
next_state.points = gesture.points;
last_gesture = gesture;
shared_memory->gesture_lifo.WriteNextEntry(next_state);
}
void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
GestureAttribute& attributes) {
const auto& last_entry = GetLastGestureEntry();
gesture.detection_count++;
type = GestureType::Touch;
// New touch after cancel is not considered new
if (last_entry.type != GestureType::Cancel) {
attributes.is_new_touch.Assign(1);
enable_press_and_tap = true;
}
}
void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
// Promote to pan type if touch moved
for (size_t id = 0; id < MAX_POINTS; id++) {
if (gesture.points[id] != last_gesture.points[id]) {
type = GestureType::Pan;
break;
}
}
// Number of fingers changed cancel the last event and clear data
if (gesture.active_points != last_gesture.active_points) {
type = GestureType::Cancel;
enable_press_and_tap = false;
gesture.active_points = 0;
gesture.mid_point = {};
gesture.points.fill({});
return;
}
// Calculate extra parameters of panning
if (type == GestureType::Pan) {
UpdatePanEvent(gesture, last_gesture, type, time_difference);
return;
}
// Promote to press type
if (last_entry.type == GestureType::Touch) {
type = GestureType::Press;
}
}
void Controller_Gesture::EndGesture(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes, f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
if (last_gesture_props.active_points != 0) {
switch (last_entry.type) {
case GestureType::Touch:
if (enable_press_and_tap) {
SetTapEvent(gesture, last_gesture_props, type, attributes);
return;
}
type = GestureType::Cancel;
force_update = true;
break;
case GestureType::Press:
case GestureType::Tap:
case GestureType::Swipe:
case GestureType::Pinch:
case GestureType::Rotate:
type = GestureType::Complete;
force_update = true;
break;
case GestureType::Pan:
EndPanEvent(gesture, last_gesture_props, type, time_difference);
break;
default:
break;
}
return;
}
if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
gesture.detection_count++;
}
}
void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes) {
type = GestureType::Tap;
gesture = last_gesture_props;
force_update = true;
f32 tap_time_difference =
static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
last_tap_timestamp = last_update_timestamp;
if (tap_time_difference < double_tap_delay) {
attributes.is_double_tap.Assign(1);
}
}
void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
next_state.delta = gesture.mid_point - last_entry.pos;
next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
last_pan_time_difference = time_difference;
// Promote to pinch type
if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
pinch_threshold) {
type = GestureType::Pinch;
next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
}
const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
(1 + (gesture.angle * last_gesture_props.angle)));
// Promote to rotate type
if (std::abs(angle_between_two_lines) > angle_threshold) {
type = GestureType::Rotate;
next_state.scale = 0;
next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
}
}
void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
next_state.vel_x =
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
next_state.vel_y =
static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
const f32 curr_vel =
std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
// Set swipe event with parameters
if (curr_vel > swipe_threshold) {
SetSwipeEvent(gesture, last_gesture_props, type);
return;
}
// End panning without swipe
type = GestureType::Complete;
next_state.vel_x = 0;
next_state.vel_y = 0;
force_update = true;
}
void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type) {
const auto& last_entry = GetLastGestureEntry();
type = GestureType::Swipe;
gesture = last_gesture_props;
force_update = true;
next_state.delta = last_entry.delta;
if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
if (next_state.delta.x > 0) {
next_state.direction = GestureDirection::Right;
return;
}
next_state.direction = GestureDirection::Left;
return;
}
if (next_state.delta.y > 0) {
next_state.direction = GestureDirection::Down;
return;
}
next_state.direction = GestureDirection::Up;
}
const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
return shared_memory->gesture_lifo.ReadCurrentEntry().state;
}
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
GestureProperties gesture;
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
gesture.active_points =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
for (size_t id = 0; id < gesture.active_points; ++id) {
const auto& [active_x, active_y] = active_fingers[id].position;
gesture.points[id] = {
.x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
};
// Hack: There is no touch in docked but games still allow it
if (Settings::values.use_docked_mode.GetValue()) {
gesture.points[id] = {
.x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
.y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
};
}
gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
}
for (size_t id = 0; id < gesture.active_points; ++id) {
const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
Square(gesture.mid_point.y - gesture.points[id].y));
gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
}
gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
gesture.detection_count = last_gesture.detection_count;
return gesture;
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/gesture.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
constexpr f32 angle_threshold = 0.015f; // Threshold in radians
constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
constexpr f32 press_delay = 0.5f; // Time in seconds
constexpr f32 double_tap_delay = 0.35f; // Time in seconds
constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num);
}
Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase(hid_core_) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
"GestureSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
force_update = true;
}
void Controller_Gesture::OnRelease() {}
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
return;
}
ReadTouchInput();
GestureProperties gesture = GetGestureProperties();
f32 time_difference =
static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
(1000 * 1000 * 1000);
// Only update if necesary
if (!ShouldUpdateGesture(gesture, time_difference)) {
return;
}
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
UpdateGestureSharedMemory(gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
const auto touch_status = console->GetTouch();
for (std::size_t id = 0; id < fingers.size(); ++id) {
fingers[id] = touch_status[id];
}
}
bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
if (force_update) {
force_update = false;
return true;
}
// Update if coordinates change
for (size_t id = 0; id < MAX_POINTS; id++) {
if (gesture.points[id] != last_gesture.points[id]) {
return true;
}
}
// Update on press and hold event after 0.5 seconds
if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
time_difference > press_delay) {
return enable_press_and_tap;
}
return false;
}
void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle;
GestureAttribute attributes{};
const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
// Reset next state to default
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.delta = {};
next_state.vel_x = 0;
next_state.vel_y = 0;
next_state.direction = GestureDirection::None;
next_state.rotation_angle = 0;
next_state.scale = 0;
if (gesture.active_points > 0) {
if (last_gesture.active_points == 0) {
NewGesture(gesture, type, attributes);
} else {
UpdateExistingGesture(gesture, type, time_difference);
}
} else {
EndGesture(gesture, last_gesture, type, attributes, time_difference);
}
// Apply attributes
next_state.detection_count = gesture.detection_count;
next_state.type = type;
next_state.attributes = attributes;
next_state.pos = gesture.mid_point;
next_state.point_count = static_cast<s32>(gesture.active_points);
next_state.points = gesture.points;
last_gesture = gesture;
shared_memory->gesture_lifo.WriteNextEntry(next_state);
}
void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
GestureAttribute& attributes) {
const auto& last_entry = GetLastGestureEntry();
gesture.detection_count++;
type = GestureType::Touch;
// New touch after cancel is not considered new
if (last_entry.type != GestureType::Cancel) {
attributes.is_new_touch.Assign(1);
enable_press_and_tap = true;
}
}
void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
// Promote to pan type if touch moved
for (size_t id = 0; id < MAX_POINTS; id++) {
if (gesture.points[id] != last_gesture.points[id]) {
type = GestureType::Pan;
break;
}
}
// Number of fingers changed cancel the last event and clear data
if (gesture.active_points != last_gesture.active_points) {
type = GestureType::Cancel;
enable_press_and_tap = false;
gesture.active_points = 0;
gesture.mid_point = {};
gesture.points.fill({});
return;
}
// Calculate extra parameters of panning
if (type == GestureType::Pan) {
UpdatePanEvent(gesture, last_gesture, type, time_difference);
return;
}
// Promote to press type
if (last_entry.type == GestureType::Touch) {
type = GestureType::Press;
}
}
void Controller_Gesture::EndGesture(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes, f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
if (last_gesture_props.active_points != 0) {
switch (last_entry.type) {
case GestureType::Touch:
if (enable_press_and_tap) {
SetTapEvent(gesture, last_gesture_props, type, attributes);
return;
}
type = GestureType::Cancel;
force_update = true;
break;
case GestureType::Press:
case GestureType::Tap:
case GestureType::Swipe:
case GestureType::Pinch:
case GestureType::Rotate:
type = GestureType::Complete;
force_update = true;
break;
case GestureType::Pan:
EndPanEvent(gesture, last_gesture_props, type, time_difference);
break;
default:
break;
}
return;
}
if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
gesture.detection_count++;
}
}
void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes) {
type = GestureType::Tap;
gesture = last_gesture_props;
force_update = true;
f32 tap_time_difference =
static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
last_tap_timestamp = last_update_timestamp;
if (tap_time_difference < double_tap_delay) {
attributes.is_double_tap.Assign(1);
}
}
void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
next_state.delta = gesture.mid_point - last_entry.pos;
next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
last_pan_time_difference = time_difference;
// Promote to pinch type
if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
pinch_threshold) {
type = GestureType::Pinch;
next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
}
const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
(1 + (gesture.angle * last_gesture_props.angle)));
// Promote to rotate type
if (std::abs(angle_between_two_lines) > angle_threshold) {
type = GestureType::Rotate;
next_state.scale = 0;
next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
}
}
void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
next_state.vel_x =
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
next_state.vel_y =
static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
const f32 curr_vel =
std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
// Set swipe event with parameters
if (curr_vel > swipe_threshold) {
SetSwipeEvent(gesture, last_gesture_props, type);
return;
}
// End panning without swipe
type = GestureType::Complete;
next_state.vel_x = 0;
next_state.vel_y = 0;
force_update = true;
}
void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type) {
const auto& last_entry = GetLastGestureEntry();
type = GestureType::Swipe;
gesture = last_gesture_props;
force_update = true;
next_state.delta = last_entry.delta;
if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
if (next_state.delta.x > 0) {
next_state.direction = GestureDirection::Right;
return;
}
next_state.direction = GestureDirection::Left;
return;
}
if (next_state.delta.y > 0) {
next_state.direction = GestureDirection::Down;
return;
}
next_state.direction = GestureDirection::Up;
}
const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
return shared_memory->gesture_lifo.ReadCurrentEntry().state;
}
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
GestureProperties gesture;
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
gesture.active_points =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
for (size_t id = 0; id < gesture.active_points; ++id) {
const auto& [active_x, active_y] = active_fingers[id].position;
gesture.points[id] = {
.x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
};
// Hack: There is no touch in docked but games still allow it
if (Settings::values.use_docked_mode.GetValue()) {
gesture.points[id] = {
.x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
.y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
};
}
gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
}
for (size_t id = 0; id < gesture.active_points; ++id) {
const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
Square(gesture.mid_point.y - gesture.points[id].y));
gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
}
gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
gesture.detection_count = last_gesture.detection_count;
return gesture;
}
} // namespace Service::HID

View File

@@ -1,156 +1,156 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/point.h"
#include "core/hid/emulated_console.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_Gesture final : public ControllerBase {
public:
explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Gesture() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
// This is nn::hid::GestureType
enum class GestureType : u32 {
Idle, // Nothing touching the screen
Complete, // Set at the end of a touch event
Cancel, // Set when the number of fingers change
Touch, // A finger just touched the screen
Press, // Set if last type is touch and the finger hasn't moved
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
// This is nn::hid::GestureDirection
enum class GestureDirection : u32 {
None,
Left,
Up,
Right,
Down,
};
// This is nn::hid::GestureAttribute
struct GestureAttribute {
union {
u32 raw{};
BitField<4, 1, u32> is_new_touch;
BitField<8, 1, u32> is_double_tap;
};
};
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
// This is nn::hid::GestureState
struct GestureState {
s64 sampling_number{};
s64 detection_count{};
GestureType type{GestureType::Idle};
GestureDirection direction{GestureDirection::None};
Common::Point<s32> pos{};
Common::Point<s32> delta{};
f32 vel_x{};
f32 vel_y{};
GestureAttribute attributes{};
f32 scale{};
f32 rotation_angle{};
s32 point_count{};
std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
struct GestureProperties {
std::array<Common::Point<s32>, MAX_POINTS> points{};
std::size_t active_points{};
Common::Point<s32> mid_point{};
s64 detection_count{};
u64 delta_time{};
f32 average_distance{};
f32 angle{};
};
struct GestureSharedMemory {
// This is nn::hid::detail::GestureLifo
Lifo<GestureState, hid_entry_count> gesture_lifo{};
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
INSERT_PADDING_WORDS(0x3E);
};
static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size");
// Reads input from all available input engines
void ReadTouchInput();
// Returns true if gesture state needs to be updated
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
// Updates the shared memory to the next state
void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
// Initializes new gesture
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
// Updates existing gesture state
void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
// Terminates exiting gesture
void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, GestureAttribute& attributes, f32 time_difference);
// Set current event to a tap event
void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, GestureAttribute& attributes);
// Calculates and set the extra parameters related to a pan event
void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, f32 time_difference);
// Terminates the pan event
void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, f32 time_difference);
// Set current event to a swipe event
void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type);
// Retrieves the last gesture entry, as indicated by shared memory indices.
[[nodiscard]] const GestureState& GetLastGestureEntry() const;
// Returns the average distance, angle and middle point of the active fingers
GestureProperties GetGestureProperties();
GestureState next_state{};
GestureSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
GestureProperties last_gesture{};
s64 last_update_timestamp{};
s64 last_tap_timestamp{};
f32 last_pan_time_difference{};
bool force_update{false};
bool enable_press_and_tap{false};
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/point.h"
#include "core/hid/emulated_console.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_Gesture final : public ControllerBase {
public:
explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Gesture() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
// This is nn::hid::GestureType
enum class GestureType : u32 {
Idle, // Nothing touching the screen
Complete, // Set at the end of a touch event
Cancel, // Set when the number of fingers change
Touch, // A finger just touched the screen
Press, // Set if last type is touch and the finger hasn't moved
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
// This is nn::hid::GestureDirection
enum class GestureDirection : u32 {
None,
Left,
Up,
Right,
Down,
};
// This is nn::hid::GestureAttribute
struct GestureAttribute {
union {
u32 raw{};
BitField<4, 1, u32> is_new_touch;
BitField<8, 1, u32> is_double_tap;
};
};
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
// This is nn::hid::GestureState
struct GestureState {
s64 sampling_number{};
s64 detection_count{};
GestureType type{GestureType::Idle};
GestureDirection direction{GestureDirection::None};
Common::Point<s32> pos{};
Common::Point<s32> delta{};
f32 vel_x{};
f32 vel_y{};
GestureAttribute attributes{};
f32 scale{};
f32 rotation_angle{};
s32 point_count{};
std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
struct GestureProperties {
std::array<Common::Point<s32>, MAX_POINTS> points{};
std::size_t active_points{};
Common::Point<s32> mid_point{};
s64 detection_count{};
u64 delta_time{};
f32 average_distance{};
f32 angle{};
};
struct GestureSharedMemory {
// This is nn::hid::detail::GestureLifo
Lifo<GestureState, hid_entry_count> gesture_lifo{};
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
INSERT_PADDING_WORDS(0x3E);
};
static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size");
// Reads input from all available input engines
void ReadTouchInput();
// Returns true if gesture state needs to be updated
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
// Updates the shared memory to the next state
void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
// Initializes new gesture
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
// Updates existing gesture state
void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
// Terminates exiting gesture
void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, GestureAttribute& attributes, f32 time_difference);
// Set current event to a tap event
void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, GestureAttribute& attributes);
// Calculates and set the extra parameters related to a pan event
void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, f32 time_difference);
// Terminates the pan event
void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, f32 time_difference);
// Set current event to a swipe event
void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type);
// Retrieves the last gesture entry, as indicated by shared memory indices.
[[nodiscard]] const GestureState& GetLastGestureEntry() const;
// Returns the average distance, angle and middle point of the active fingers
GestureProperties GetGestureProperties();
GestureState next_state{};
GestureSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
GestureProperties last_gesture{};
s64 last_update_timestamp{};
s64 last_tap_timestamp{};
f32 last_pan_time_difference{};
bool force_update{false};
bool enable_press_and_tap{false};
};
} // namespace Service::HID

View File

@@ -1,52 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/keyboard.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
"KeyboardSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
void Controller_Keyboard::OnRelease() {}
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->keyboard_lifo.buffer_count = 0;
shared_memory->keyboard_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.keyboard_enabled) {
const auto& keyboard_state = emulated_devices->GetKeyboard();
const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
next_state.key = keyboard_state;
next_state.modifier = keyboard_modifier_state;
next_state.attribute.is_connected.Assign(1);
}
shared_memory->keyboard_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/keyboard.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
"KeyboardSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
void Controller_Keyboard::OnRelease() {}
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->keyboard_lifo.buffer_count = 0;
shared_memory->keyboard_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.keyboard_enabled) {
const auto& keyboard_state = emulated_devices->GetKeyboard();
const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
next_state.key = keyboard_state;
next_state.modifier = keyboard_modifier_state;
next_state.attribute.is_connected.Assign(1);
}
shared_memory->keyboard_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -1,53 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
struct KeyboardModifier;
struct KeyboardKey;
} // namespace Core::HID
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
public:
explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Keyboard() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::detail::KeyboardState
struct KeyboardState {
s64 sampling_number{};
Core::HID::KeyboardModifier modifier{};
Core::HID::KeyboardAttribute attribute{};
Core::HID::KeyboardKey key{};
};
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
struct KeyboardSharedMemory {
// This is nn::hid::detail::KeyboardLifo
Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
INSERT_PADDING_WORDS(0xA);
};
static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size");
KeyboardState next_state{};
KeyboardSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
struct KeyboardModifier;
struct KeyboardKey;
} // namespace Core::HID
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
public:
explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Keyboard() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::detail::KeyboardState
struct KeyboardState {
s64 sampling_number{};
Core::HID::KeyboardModifier modifier{};
Core::HID::KeyboardAttribute attribute{};
Core::HID::KeyboardKey key{};
};
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
struct KeyboardSharedMemory {
// This is nn::hid::detail::KeyboardLifo
Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
INSERT_PADDING_WORDS(0xA);
};
static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size");
KeyboardState next_state{};
KeyboardSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID

View File

@@ -1,59 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
"MouseSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->mouse_lifo.buffer_count = 0;
shared_memory->mouse_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.attribute.raw = 0;
if (Settings::values.mouse_enabled) {
const auto& mouse_button_state = emulated_devices->GetMouseButtons();
const auto& mouse_position_state = emulated_devices->GetMousePosition();
const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
next_state.attribute.is_connected.Assign(1);
next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
next_state.delta_x = next_state.x - last_entry.x;
next_state.delta_y = next_state.y - last_entry.y;
next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
last_mouse_wheel_state = mouse_wheel_state;
next_state.button = mouse_button_state;
}
shared_memory->mouse_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
"MouseSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->mouse_lifo.buffer_count = 0;
shared_memory->mouse_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.attribute.raw = 0;
if (Settings::values.mouse_enabled) {
const auto& mouse_button_state = emulated_devices->GetMouseButtons();
const auto& mouse_position_state = emulated_devices->GetMousePosition();
const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
next_state.attribute.is_connected.Assign(1);
next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
next_state.delta_x = next_state.x - last_entry.x;
next_state.delta_y = next_state.y - last_entry.y;
next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
last_mouse_wheel_state = mouse_wheel_state;
next_state.button = mouse_button_state;
}
shared_memory->mouse_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -1,45 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
struct MouseState;
struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
public:
explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Mouse() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
struct MouseSharedMemory {
// This is nn::hid::detail::MouseLifo
Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
INSERT_PADDING_WORDS(0x2C);
};
static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size");
Core::HID::MouseState next_state{};
Core::HID::AnalogStickState last_mouse_wheel_state{};
MouseSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
struct MouseState;
struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
public:
explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Mouse() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
struct MouseSharedMemory {
// This is nn::hid::detail::MouseLifo
Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
INSERT_PADDING_WORDS(0x2C);
};
static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size");
Core::HID::MouseState next_state{};
Core::HID::AnalogStickState last_mouse_wheel_state{};
MouseSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,229 +1,229 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
}
Controller_Palma::~Controller_Palma() = default;
void Controller_Palma::OnInit() {}
void Controller_Palma::OnRelease() {}
void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
}
Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
PalmaConnectionHandle& handle) {
active_handle.npad_id = npad_id;
handle = active_handle;
return ResultSuccess;
}
Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
ActivateController();
return ResultSuccess;
}
Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) {
LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
}
return operation_complete_event->GetReadableEvent();
}
Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
PalmaOperationType& operation_type,
PalmaOperationData& data) const {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation_type = operation.operation;
data = operation.data;
return ResultSuccess;
}
Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
u64 palma_activity) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::PlayActivity;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
PalmaFrModeType fr_mode_) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
fr_mode = fr_mode_;
return ResultSuccess;
}
Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadStep;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return ResultSuccess;
}
Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return ResultSuccess;
}
void Controller_Palma::ReadPalmaApplicationSection() {}
void Controller_Palma::WritePalmaApplicationSection() {}
Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadUniqueCode;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::SetUniqueCodeInvalid;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
void Controller_Palma::WritePalmaActivityEntry() {}
Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
u64 unknown) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::WriteRgbLedPatternEntry;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
u8* t_mem, u64 size) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::WriteWaveEntry;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
s32 database_id_version_) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
database_id_version = database_id_version_;
operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
operation.result = PalmaResultSuccess;
operation.data[0] = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
operation.result = PalmaResultSuccess;
operation.data = {};
operation.data[0] = static_cast<u8>(database_id_version);
operation_complete_event->Signal();
return ResultSuccess;
}
void Controller_Palma::SuspendPalmaFeature() {}
Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return operation.result;
}
void Controller_Palma::ReadPalmaPlayLog() {}
void Controller_Palma::ResetPalmaPlayLog() {}
void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
// If true controllers are able to be paired
is_connectable = is_all_connectable;
}
void Controller_Palma::SetIsPalmaPairedConnectable() {}
Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
// TODO: Do something
return ResultSuccess;
}
void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {}
void Controller_Palma::CancelWritePalmaWaveEntry() {}
void Controller_Palma::EnablePalmaBoostMode() {}
void Controller_Palma::GetPalmaBluetoothAddress() {}
void Controller_Palma::SetDisallowedPalmaConnection() {}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
}
Controller_Palma::~Controller_Palma() = default;
void Controller_Palma::OnInit() {}
void Controller_Palma::OnRelease() {}
void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
}
Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
PalmaConnectionHandle& handle) {
active_handle.npad_id = npad_id;
handle = active_handle;
return ResultSuccess;
}
Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
ActivateController();
return ResultSuccess;
}
Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) {
LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
}
return operation_complete_event->GetReadableEvent();
}
Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
PalmaOperationType& operation_type,
PalmaOperationData& data) const {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation_type = operation.operation;
data = operation.data;
return ResultSuccess;
}
Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
u64 palma_activity) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::PlayActivity;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
PalmaFrModeType fr_mode_) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
fr_mode = fr_mode_;
return ResultSuccess;
}
Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadStep;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return ResultSuccess;
}
Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return ResultSuccess;
}
void Controller_Palma::ReadPalmaApplicationSection() {}
void Controller_Palma::WritePalmaApplicationSection() {}
Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadUniqueCode;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::SetUniqueCodeInvalid;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
void Controller_Palma::WritePalmaActivityEntry() {}
Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
u64 unknown) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::WriteRgbLedPatternEntry;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
u8* t_mem, u64 size) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::WriteWaveEntry;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
s32 database_id_version_) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
database_id_version = database_id_version_;
operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
operation.result = PalmaResultSuccess;
operation.data[0] = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
operation.result = PalmaResultSuccess;
operation.data = {};
operation.data[0] = static_cast<u8>(database_id_version);
operation_complete_event->Signal();
return ResultSuccess;
}
void Controller_Palma::SuspendPalmaFeature() {}
Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return operation.result;
}
void Controller_Palma::ReadPalmaPlayLog() {}
void Controller_Palma::ResetPalmaPlayLog() {}
void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
// If true controllers are able to be paired
is_connectable = is_all_connectable;
}
void Controller_Palma::SetIsPalmaPairedConnectable() {}
Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
// TODO: Do something
return ResultSuccess;
}
void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {}
void Controller_Palma::CancelWritePalmaWaveEntry() {}
void Controller_Palma::EnablePalmaBoostMode() {}
void Controller_Palma::GetPalmaBluetoothAddress() {}
void Controller_Palma::SetDisallowedPalmaConnection() {}
} // namespace Service::HID

View File

@@ -1,163 +1,163 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/errors.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::HID {
class Controller_Palma final : public ControllerBase {
public:
using PalmaOperationData = std::array<u8, 0x140>;
// This is nn::hid::PalmaOperationType
enum class PalmaOperationType {
PlayActivity,
SetFrModeType,
ReadStep,
EnableStep,
ResetStep,
ReadApplicationSection,
WriteApplicationSection,
ReadUniqueCode,
SetUniqueCodeInvalid,
WriteActivityEntry,
WriteRgbLedPatternEntry,
WriteWaveEntry,
ReadDataBaseIdentificationVersion,
WriteDataBaseIdentificationVersion,
SuspendFeature,
ReadPlayLog,
ResetPlayLog,
};
// This is nn::hid::PalmaWaveSet
enum class PalmaWaveSet : u64 {
Small,
Medium,
Large,
};
// This is nn::hid::PalmaFrModeType
enum class PalmaFrModeType : u64 {
Off,
B01,
B02,
B03,
Downloaded,
};
// This is nn::hid::PalmaFeature
enum class PalmaFeature : u64 {
FrMode,
RumbleFeedback,
Step,
MuteSwitch,
};
// This is nn::hid::PalmaOperationInfo
struct PalmaOperationInfo {
PalmaOperationType operation{};
Result result{PalmaResultSuccess};
PalmaOperationData data{};
};
static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size");
// This is nn::hid::PalmaActivityEntry
struct PalmaActivityEntry {
u32 rgb_led_pattern_index;
INSERT_PADDING_BYTES(2);
PalmaWaveSet wave_set;
u32 wave_index;
INSERT_PADDING_BYTES(12);
};
static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size");
struct PalmaConnectionHandle {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_BYTES(4); // Unknown
};
static_assert(sizeof(PalmaConnectionHandle) == 0x8,
"PalmaConnectionHandle has incorrect size.");
explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_);
~Controller_Palma() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle);
Result InitializePalma(const PalmaConnectionHandle& handle);
Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent(
const PalmaConnectionHandle& handle) const;
Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
PalmaOperationType& operation_type,
PalmaOperationData& data) const;
Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity);
Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_);
Result ReadPalmaStep(const PalmaConnectionHandle& handle);
Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled);
Result ResetPalmaStep(const PalmaConnectionHandle& handle);
Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle);
Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle);
Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown);
Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem,
u64 size);
Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
s32 database_id_version_);
Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle);
Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const;
void SetIsPalmaAllConnectable(bool is_all_connectable);
Result PairPalma(const PalmaConnectionHandle& handle);
void SetPalmaBoostMode(bool boost_mode);
private:
void ReadPalmaApplicationSection();
void WritePalmaApplicationSection();
void WritePalmaActivityEntry();
void SuspendPalmaFeature();
void ReadPalmaPlayLog();
void ResetPalmaPlayLog();
void SetIsPalmaPairedConnectable();
void CancelWritePalmaWaveEntry();
void EnablePalmaBoostMode();
void GetPalmaBluetoothAddress();
void SetDisallowedPalmaConnection();
bool is_connectable{};
s32 database_id_version{};
PalmaOperationInfo operation{};
PalmaFrModeType fr_mode{};
PalmaConnectionHandle active_handle{};
Core::HID::EmulatedController* controller;
Kernel::KEvent* operation_complete_event;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/errors.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::HID {
class Controller_Palma final : public ControllerBase {
public:
using PalmaOperationData = std::array<u8, 0x140>;
// This is nn::hid::PalmaOperationType
enum class PalmaOperationType {
PlayActivity,
SetFrModeType,
ReadStep,
EnableStep,
ResetStep,
ReadApplicationSection,
WriteApplicationSection,
ReadUniqueCode,
SetUniqueCodeInvalid,
WriteActivityEntry,
WriteRgbLedPatternEntry,
WriteWaveEntry,
ReadDataBaseIdentificationVersion,
WriteDataBaseIdentificationVersion,
SuspendFeature,
ReadPlayLog,
ResetPlayLog,
};
// This is nn::hid::PalmaWaveSet
enum class PalmaWaveSet : u64 {
Small,
Medium,
Large,
};
// This is nn::hid::PalmaFrModeType
enum class PalmaFrModeType : u64 {
Off,
B01,
B02,
B03,
Downloaded,
};
// This is nn::hid::PalmaFeature
enum class PalmaFeature : u64 {
FrMode,
RumbleFeedback,
Step,
MuteSwitch,
};
// This is nn::hid::PalmaOperationInfo
struct PalmaOperationInfo {
PalmaOperationType operation{};
Result result{PalmaResultSuccess};
PalmaOperationData data{};
};
static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size");
// This is nn::hid::PalmaActivityEntry
struct PalmaActivityEntry {
u32 rgb_led_pattern_index;
INSERT_PADDING_BYTES(2);
PalmaWaveSet wave_set;
u32 wave_index;
INSERT_PADDING_BYTES(12);
};
static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size");
struct PalmaConnectionHandle {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_BYTES(4); // Unknown
};
static_assert(sizeof(PalmaConnectionHandle) == 0x8,
"PalmaConnectionHandle has incorrect size.");
explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_);
~Controller_Palma() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle);
Result InitializePalma(const PalmaConnectionHandle& handle);
Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent(
const PalmaConnectionHandle& handle) const;
Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
PalmaOperationType& operation_type,
PalmaOperationData& data) const;
Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity);
Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_);
Result ReadPalmaStep(const PalmaConnectionHandle& handle);
Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled);
Result ResetPalmaStep(const PalmaConnectionHandle& handle);
Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle);
Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle);
Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown);
Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem,
u64 size);
Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
s32 database_id_version_);
Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle);
Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const;
void SetIsPalmaAllConnectable(bool is_all_connectable);
Result PairPalma(const PalmaConnectionHandle& handle);
void SetPalmaBoostMode(bool boost_mode);
private:
void ReadPalmaApplicationSection();
void WritePalmaApplicationSection();
void WritePalmaActivityEntry();
void SuspendPalmaFeature();
void ReadPalmaPlayLog();
void ResetPalmaPlayLog();
void SetIsPalmaPairedConnectable();
void CancelWritePalmaWaveEntry();
void EnablePalmaBoostMode();
void GetPalmaBluetoothAddress();
void SetDisallowedPalmaConnection();
bool is_connectable{};
s32 database_id_version{};
PalmaOperationInfo operation{};
PalmaFrModeType fr_mode{};
PalmaConnectionHandle active_handle{};
Core::HID::EmulatedController* controller;
Kernel::KEvent* operation_complete_event;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID

View File

@@ -1,42 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/stubbed.h"
namespace Service::HID {
Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
raw_shared_memory = raw_shared_memory_;
}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
void Controller_Stubbed::OnRelease() {}
void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
CommonHeader header{};
header.timestamp = core_timing.GetCPUTicks();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));
}
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
common_offset = off;
smart_update = true;
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/stubbed.h"
namespace Service::HID {
Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
raw_shared_memory = raw_shared_memory_;
}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
void Controller_Stubbed::OnRelease() {}
void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
CommonHeader header{};
header.timestamp = core_timing.GetCPUTicks();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));
}
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
common_offset = off;
smart_update = true;
}
} // namespace Service::HID

View File

@@ -1,39 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Stubbed final : public ControllerBase {
public:
explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Stubbed() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
void SetCommonHeaderOffset(std::size_t off);
private:
struct CommonHeader {
s64 timestamp{};
s64 total_entry_count{};
s64 last_entry_index{};
s64 entry_count{};
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
u8* raw_shared_memory = nullptr;
bool smart_update{};
std::size_t common_offset{};
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Stubbed final : public ControllerBase {
public:
explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Stubbed() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
void SetCommonHeaderOffset(std::size_t off);
private:
struct CommonHeader {
s64 timestamp{};
s64 total_entry_count{};
s64 last_entry_index{};
s64 entry_count{};
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
u8* raw_shared_memory = nullptr;
bool smart_update{};
std::size_t common_offset{};
};
} // namespace Service::HID

View File

@@ -1,119 +1,119 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
"TouchSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}
void Controller_Touchscreen::OnRelease() {}
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
shared_memory->touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
if (!IsControllerActivated()) {
shared_memory->touch_screen_lifo.buffer_count = 0;
shared_memory->touch_screen_lifo.buffer_tail = 0;
return;
}
const auto touch_status = console->GetTouch();
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
const auto& current_touch = touch_status[id];
auto& finger = fingers[id];
finger.id = current_touch.id;
if (finger.attribute.start_touch) {
finger.attribute.raw = 0;
continue;
}
if (finger.attribute.end_touch) {
finger.attribute.raw = 0;
finger.pressed = false;
continue;
}
if (!finger.pressed && current_touch.pressed) {
finger.attribute.start_touch.Assign(1);
finger.pressed = true;
finger.position = current_touch.position;
continue;
}
if (finger.pressed && !current_touch.pressed) {
finger.attribute.raw = 0;
finger.attribute.end_touch.Assign(1);
continue;
}
// Only update position if touch is not on a special frame
finger.position = current_touch.position;
}
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
const auto active_fingers_count =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
const u64 tick = core_timing.GetCPUTicks();
const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.entry_count = static_cast<s32>(active_fingers_count);
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
auto& touch_entry = next_state.states[id];
if (id < active_fingers_count) {
const auto& [active_x, active_y] = active_fingers[id].position;
touch_entry.position = {
.x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height),
};
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
touch_entry.delta_time = tick - active_fingers[id].last_touch;
fingers[active_fingers[id].id].last_touch = tick;
touch_entry.finger = active_fingers[id].id;
touch_entry.attribute.raw = active_fingers[id].attribute.raw;
} else {
// Clear touch entry
touch_entry.attribute.raw = 0;
touch_entry.position = {};
touch_entry.diameter_x = 0;
touch_entry.diameter_y = 0;
touch_entry.rotation_angle = 0;
touch_entry.delta_time = 0;
touch_entry.finger = 0;
}
}
shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
"TouchSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}
void Controller_Touchscreen::OnRelease() {}
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
shared_memory->touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
if (!IsControllerActivated()) {
shared_memory->touch_screen_lifo.buffer_count = 0;
shared_memory->touch_screen_lifo.buffer_tail = 0;
return;
}
const auto touch_status = console->GetTouch();
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
const auto& current_touch = touch_status[id];
auto& finger = fingers[id];
finger.id = current_touch.id;
if (finger.attribute.start_touch) {
finger.attribute.raw = 0;
continue;
}
if (finger.attribute.end_touch) {
finger.attribute.raw = 0;
finger.pressed = false;
continue;
}
if (!finger.pressed && current_touch.pressed) {
finger.attribute.start_touch.Assign(1);
finger.pressed = true;
finger.position = current_touch.position;
continue;
}
if (finger.pressed && !current_touch.pressed) {
finger.attribute.raw = 0;
finger.attribute.end_touch.Assign(1);
continue;
}
// Only update position if touch is not on a special frame
finger.position = current_touch.position;
}
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
const auto active_fingers_count =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
const u64 tick = core_timing.GetCPUTicks();
const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.entry_count = static_cast<s32>(active_fingers_count);
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
auto& touch_entry = next_state.states[id];
if (id < active_fingers_count) {
const auto& [active_x, active_y] = active_fingers[id].position;
touch_entry.position = {
.x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height),
};
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
touch_entry.delta_time = tick - active_fingers[id].last_touch;
fingers[active_fingers[id].id].last_touch = tick;
touch_entry.finger = active_fingers[id].id;
touch_entry.attribute.raw = active_fingers[id].attribute.raw;
} else {
// Clear touch entry
touch_entry.attribute.raw = 0;
touch_entry.position = {};
touch_entry.diameter_x = 0;
touch_entry.diameter_y = 0;
touch_entry.rotation_angle = 0;
touch_entry.delta_time = 0;
touch_entry.finger = 0;
}
}
shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -1,73 +1,73 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase {
public:
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES_NOINIT(0x7);
INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
"TouchScreenConfigurationForNx is an invalid size");
explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Touchscreen() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr std::size_t MAX_FINGERS = 16;
// This is nn::hid::TouchScreenState
struct TouchScreenState {
s64 sampling_number{};
s32 entry_count{};
INSERT_PADDING_BYTES(4); // Reserved
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
};
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
struct TouchSharedMemory {
// This is nn::hid::detail::TouchScreenLifo
Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
INSERT_PADDING_WORDS(0xF2);
};
static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size");
TouchScreenState next_state{};
TouchSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase {
public:
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES_NOINIT(0x7);
INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
"TouchScreenConfigurationForNx is an invalid size");
explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Touchscreen() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr std::size_t MAX_FINGERS = 16;
// This is nn::hid::TouchScreenState
struct TouchScreenState {
s64 sampling_number{};
s32 entry_count{};
INSERT_PADDING_BYTES(4); // Reserved
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
};
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
struct TouchSharedMemory {
// This is nn::hid::detail::TouchScreenLifo
Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
INSERT_PADDING_WORDS(0xF2);
};
static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size");
TouchScreenState next_state{};
TouchSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
};
} // namespace Service::HID

View File

@@ -1,40 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
"XpadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
void Controller_XPad::OnRelease() {}
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->basic_xpad_lifo.buffer_count = 0;
shared_memory->basic_xpad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
// TODO(ogniK): Update xpad states
shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
"XpadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
void Controller_XPad::OnRelease() {}
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->basic_xpad_lifo.buffer_count = 0;
shared_memory->basic_xpad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
// TODO(ogniK): Update xpad states
shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -1,112 +1,112 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_XPad final : public ControllerBase {
public:
explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_XPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::BasicXpadAttributeSet
struct BasicXpadAttributeSet {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
// This is nn::hid::BasicXpadButtonSet
struct BasicXpadButtonSet {
union {
u32 raw{};
// Button states
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
BitField<2, 1, u32> x;
BitField<3, 1, u32> y;
BitField<4, 1, u32> l_stick;
BitField<5, 1, u32> r_stick;
BitField<6, 1, u32> l;
BitField<7, 1, u32> r;
BitField<8, 1, u32> zl;
BitField<9, 1, u32> zr;
BitField<10, 1, u32> plus;
BitField<11, 1, u32> minus;
// D-Pad
BitField<12, 1, u32> d_left;
BitField<13, 1, u32> d_up;
BitField<14, 1, u32> d_right;
BitField<15, 1, u32> d_down;
// Left JoyStick
BitField<16, 1, u32> l_stick_left;
BitField<17, 1, u32> l_stick_up;
BitField<18, 1, u32> l_stick_right;
BitField<19, 1, u32> l_stick_down;
// Right JoyStick
BitField<20, 1, u32> r_stick_left;
BitField<21, 1, u32> r_stick_up;
BitField<22, 1, u32> r_stick_right;
BitField<23, 1, u32> r_stick_down;
// Not always active?
BitField<24, 1, u32> left_sl;
BitField<25, 1, u32> left_sr;
BitField<26, 1, u32> right_sl;
BitField<27, 1, u32> right_sr;
BitField<28, 1, u32> palma;
BitField<30, 1, u32> handheld_left_b;
};
};
static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
// This is nn::hid::detail::BasicXpadState
struct BasicXpadState {
s64 sampling_number{};
BasicXpadAttributeSet attributes{};
BasicXpadButtonSet pad_states{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
};
static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
struct XpadSharedMemory {
// This is nn::hid::detail::BasicXpadLifo
Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size");
BasicXpadState next_state{};
XpadSharedMemory* shared_memory = nullptr;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_XPad final : public ControllerBase {
public:
explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_XPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::BasicXpadAttributeSet
struct BasicXpadAttributeSet {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
// This is nn::hid::BasicXpadButtonSet
struct BasicXpadButtonSet {
union {
u32 raw{};
// Button states
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
BitField<2, 1, u32> x;
BitField<3, 1, u32> y;
BitField<4, 1, u32> l_stick;
BitField<5, 1, u32> r_stick;
BitField<6, 1, u32> l;
BitField<7, 1, u32> r;
BitField<8, 1, u32> zl;
BitField<9, 1, u32> zr;
BitField<10, 1, u32> plus;
BitField<11, 1, u32> minus;
// D-Pad
BitField<12, 1, u32> d_left;
BitField<13, 1, u32> d_up;
BitField<14, 1, u32> d_right;
BitField<15, 1, u32> d_down;
// Left JoyStick
BitField<16, 1, u32> l_stick_left;
BitField<17, 1, u32> l_stick_up;
BitField<18, 1, u32> l_stick_right;
BitField<19, 1, u32> l_stick_down;
// Right JoyStick
BitField<20, 1, u32> r_stick_left;
BitField<21, 1, u32> r_stick_up;
BitField<22, 1, u32> r_stick_right;
BitField<23, 1, u32> r_stick_down;
// Not always active?
BitField<24, 1, u32> left_sl;
BitField<25, 1, u32> left_sr;
BitField<26, 1, u32> right_sl;
BitField<27, 1, u32> right_sr;
BitField<28, 1, u32> palma;
BitField<30, 1, u32> handheld_left_b;
};
};
static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
// This is nn::hid::detail::BasicXpadState
struct BasicXpadState {
s64 sampling_number{};
BasicXpadAttributeSet attributes{};
BasicXpadButtonSet pad_states{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
};
static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
struct XpadSharedMemory {
// This is nn::hid::detail::BasicXpadLifo
Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size");
BasicXpadState next_state{};
XpadSharedMemory* shared_memory = nullptr;
};
} // namespace Service::HID

View File

@@ -1,30 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::HID {
constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123};
constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
constexpr Result NpadIsSameType{ErrorModule::HID, 602};
constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
} // namespace Service::HID
namespace Service::IRS {
constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::HID {
constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123};
constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
constexpr Result NpadIsSameType{ErrorModule::HID, 602};
constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
} // namespace Service::HID
namespace Service::IRS {
constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
} // namespace Service::IRS

File diff suppressed because it is too large Load Diff

View File

@@ -1,213 +1,213 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core::Timing {
struct EventType;
}
namespace Service::SM {
class ServiceManager;
}
namespace Service::HID {
enum class HidController : std::size_t {
DebugPad,
Touchscreen,
Mouse,
Keyboard,
XPad,
HomeButton,
SleepButton,
CaptureButton,
InputDetector,
UniquePad,
NPad,
Gesture,
ConsoleSixAxisSensor,
Palma,
MaxControllers,
};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
explicit IAppletResource(Core::System& system_,
KernelHelpers::ServiceContext& service_context_);
~IAppletResource() override;
void ActivateController(HidController controller);
void DeactivateController(HidController controller);
template <typename T>
T& GetController(HidController controller) {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
template <typename T>
const T& GetController(HidController controller) const {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
private:
template <typename T>
void MakeController(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory);
}
template <typename T>
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
KernelHelpers::ServiceContext& service_context;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
};
class Hid final : public ServiceFramework<Hid> {
public:
explicit Hid(Core::System& system_);
~Hid() override;
std::shared_ptr<IAppletResource> GetAppletResource();
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
void ActivateNpad(Kernel::HLERequestContext& ctx);
void DeactivateNpad(Kernel::HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx);
void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx);
void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx);
void InitializePalma(Kernel::HLERequestContext& ctx);
void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx);
void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx);
void PlayPalmaActivity(Kernel::HLERequestContext& ctx);
void SetPalmaFrModeType(Kernel::HLERequestContext& ctx);
void ReadPalmaStep(Kernel::HLERequestContext& ctx);
void EnablePalmaStep(Kernel::HLERequestContext& ctx);
void ResetPalmaStep(Kernel::HLERequestContext& ctx);
void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx);
void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx);
void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx);
void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx);
void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx);
void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx);
void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
void SuspendPalmaFeature(Kernel::HLERequestContext& ctx);
void GetPalmaOperationResult(Kernel::HLERequestContext& ctx);
void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx);
void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx);
void PairPalma(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx);
void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx);
void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx);
void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
KernelHelpers::ServiceContext service_context;
};
/// Registers all HID services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core::Timing {
struct EventType;
}
namespace Service::SM {
class ServiceManager;
}
namespace Service::HID {
enum class HidController : std::size_t {
DebugPad,
Touchscreen,
Mouse,
Keyboard,
XPad,
HomeButton,
SleepButton,
CaptureButton,
InputDetector,
UniquePad,
NPad,
Gesture,
ConsoleSixAxisSensor,
Palma,
MaxControllers,
};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
explicit IAppletResource(Core::System& system_,
KernelHelpers::ServiceContext& service_context_);
~IAppletResource() override;
void ActivateController(HidController controller);
void DeactivateController(HidController controller);
template <typename T>
T& GetController(HidController controller) {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
template <typename T>
const T& GetController(HidController controller) const {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
private:
template <typename T>
void MakeController(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory);
}
template <typename T>
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
KernelHelpers::ServiceContext& service_context;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
};
class Hid final : public ServiceFramework<Hid> {
public:
explicit Hid(Core::System& system_);
~Hid() override;
std::shared_ptr<IAppletResource> GetAppletResource();
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
void ActivateNpad(Kernel::HLERequestContext& ctx);
void DeactivateNpad(Kernel::HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx);
void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx);
void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx);
void InitializePalma(Kernel::HLERequestContext& ctx);
void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx);
void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx);
void PlayPalmaActivity(Kernel::HLERequestContext& ctx);
void SetPalmaFrModeType(Kernel::HLERequestContext& ctx);
void ReadPalmaStep(Kernel::HLERequestContext& ctx);
void EnablePalmaStep(Kernel::HLERequestContext& ctx);
void ResetPalmaStep(Kernel::HLERequestContext& ctx);
void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx);
void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx);
void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx);
void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx);
void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx);
void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx);
void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
void SuspendPalmaFeature(Kernel::HLERequestContext& ctx);
void GetPalmaOperationResult(Kernel::HLERequestContext& ctx);
void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx);
void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx);
void PairPalma(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx);
void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx);
void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx);
void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
KernelHelpers::ServiceContext service_context;
};
/// Registers all HID services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::HID

File diff suppressed because it is too large Load Diff

View File

@@ -1,130 +1,130 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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);
Result 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
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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);
Result 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

View File

@@ -1,71 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
: service_context(service_context_) {
send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
}
HidbusBase::~HidbusBase() = default;
void HidbusBase::ActivateDevice() {
if (is_activated) {
return;
}
is_activated = true;
OnInit();
}
void HidbusBase::DeactivateDevice() {
if (is_activated) {
OnRelease();
}
is_activated = false;
}
bool HidbusBase::IsDeviceActivated() const {
return is_activated;
}
void HidbusBase::Enable(bool enable) {
device_enabled = enable;
}
bool HidbusBase::IsEnabled() const {
return device_enabled;
}
bool HidbusBase::IsPollingMode() const {
return polling_mode_enabled;
}
JoyPollingMode HidbusBase::GetPollingMode() const {
return polling_mode;
}
void HidbusBase::SetPollingMode(JoyPollingMode mode) {
polling_mode = mode;
polling_mode_enabled = true;
}
void HidbusBase::DisablePollingMode() {
polling_mode_enabled = false;
}
void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
is_transfer_memory_set = true;
transfer_memory = t_mem;
}
Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
return send_command_async_event->GetReadableEvent();
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
: service_context(service_context_) {
send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
}
HidbusBase::~HidbusBase() = default;
void HidbusBase::ActivateDevice() {
if (is_activated) {
return;
}
is_activated = true;
OnInit();
}
void HidbusBase::DeactivateDevice() {
if (is_activated) {
OnRelease();
}
is_activated = false;
}
bool HidbusBase::IsDeviceActivated() const {
return is_activated;
}
void HidbusBase::Enable(bool enable) {
device_enabled = enable;
}
bool HidbusBase::IsEnabled() const {
return device_enabled;
}
bool HidbusBase::IsPollingMode() const {
return polling_mode_enabled;
}
JoyPollingMode HidbusBase::GetPollingMode() const {
return polling_mode;
}
void HidbusBase::SetPollingMode(JoyPollingMode mode) {
polling_mode = mode;
polling_mode_enabled = true;
}
void HidbusBase::DisablePollingMode() {
polling_mode_enabled = false;
}
void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
is_transfer_memory_set = true;
transfer_memory = t_mem;
}
Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
return send_command_async_event->GetReadableEvent();
}
} // namespace Service::HID

View File

@@ -1,178 +1,178 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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 {
Result result{ResultUnknown};
INSERT_PADDING_WORDS(0x1);
std::array<u8, 0x18> unused{};
u64 latest_entry{};
u64 total_entries{};
};
static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
struct JoyDisableSixAxisPollingData {
std::array<u8, 0x26> data;
u8 out_size;
INSERT_PADDING_BYTES(0x1);
u64 sampling_number;
};
static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
"JoyDisableSixAxisPollingData is an invalid size");
struct JoyEnableSixAxisPollingData {
std::array<u8, 0x8> data;
u8 out_size;
INSERT_PADDING_BYTES(0x7);
u64 sampling_number;
};
static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
"JoyEnableSixAxisPollingData is an invalid size");
struct JoyButtonOnlyPollingData {
std::array<u8, 0x2c> data;
u8 out_size;
INSERT_PADDING_BYTES(0x3);
u64 sampling_number;
};
static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
"JoyButtonOnlyPollingData is an invalid size");
struct JoyDisableSixAxisPollingEntry {
u64 sampling_number;
JoyDisableSixAxisPollingData polling_data;
};
static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
"JoyDisableSixAxisPollingEntry is an invalid size");
struct JoyEnableSixAxisPollingEntry {
u64 sampling_number;
JoyEnableSixAxisPollingData polling_data;
};
static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
"JoyEnableSixAxisPollingEntry is an invalid size");
struct JoyButtonOnlyPollingEntry {
u64 sampling_number;
JoyButtonOnlyPollingData polling_data;
};
static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
"JoyButtonOnlyPollingEntry is an invalid size");
struct JoyDisableSixAxisDataAccessor {
DataAccessorHeader header{};
std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
};
static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
"JoyDisableSixAxisDataAccessor is an invalid size");
struct JoyEnableSixAxisDataAccessor {
DataAccessorHeader header{};
std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
};
static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
"JoyEnableSixAxisDataAccessor is an invalid size");
struct ButtonOnlyPollingDataAccessor {
DataAccessorHeader header;
std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
};
static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
"ButtonOnlyPollingDataAccessor is an invalid size");
class HidbusBase {
public:
explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
virtual ~HidbusBase();
void ActivateDevice();
void DeactivateDevice();
bool IsDeviceActivated() const;
// Enables/disables the device
void Enable(bool enable);
// returns true if device is enabled
bool IsEnabled() const;
// returns true if polling mode is enabled
bool IsPollingMode() const;
// returns polling mode
JoyPollingMode GetPollingMode() const;
// Sets and enables JoyPollingMode
void SetPollingMode(JoyPollingMode mode);
// Disables JoyPollingMode
void DisablePollingMode();
// Called on EnableJoyPollingReceiveMode
void SetTransferMemoryPointer(u8* t_mem);
Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
virtual void OnInit() {}
virtual void OnRelease() {}
// Updates device transfer memory
virtual void OnUpdate() {}
// Returns the device ID of the joycon
virtual u8 GetDeviceId() const {
return {};
}
// Assigns a command from data
virtual bool SetCommand(const std::vector<u8>& data) {
return {};
}
// Returns a reply from a command
virtual std::vector<u8> GetReply() const {
return {};
}
protected:
bool is_activated{};
bool device_enabled{};
bool polling_mode_enabled{};
JoyPollingMode polling_mode = {};
// TODO(German77): All data accessors need to be replaced with a ring lifo object
JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
ButtonOnlyPollingDataAccessor button_only_data{};
u8* transfer_memory{nullptr};
bool is_transfer_memory_set{};
Kernel::KEvent* send_command_async_event;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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 {
Result result{ResultUnknown};
INSERT_PADDING_WORDS(0x1);
std::array<u8, 0x18> unused{};
u64 latest_entry{};
u64 total_entries{};
};
static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
struct JoyDisableSixAxisPollingData {
std::array<u8, 0x26> data;
u8 out_size;
INSERT_PADDING_BYTES(0x1);
u64 sampling_number;
};
static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
"JoyDisableSixAxisPollingData is an invalid size");
struct JoyEnableSixAxisPollingData {
std::array<u8, 0x8> data;
u8 out_size;
INSERT_PADDING_BYTES(0x7);
u64 sampling_number;
};
static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
"JoyEnableSixAxisPollingData is an invalid size");
struct JoyButtonOnlyPollingData {
std::array<u8, 0x2c> data;
u8 out_size;
INSERT_PADDING_BYTES(0x3);
u64 sampling_number;
};
static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
"JoyButtonOnlyPollingData is an invalid size");
struct JoyDisableSixAxisPollingEntry {
u64 sampling_number;
JoyDisableSixAxisPollingData polling_data;
};
static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
"JoyDisableSixAxisPollingEntry is an invalid size");
struct JoyEnableSixAxisPollingEntry {
u64 sampling_number;
JoyEnableSixAxisPollingData polling_data;
};
static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
"JoyEnableSixAxisPollingEntry is an invalid size");
struct JoyButtonOnlyPollingEntry {
u64 sampling_number;
JoyButtonOnlyPollingData polling_data;
};
static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
"JoyButtonOnlyPollingEntry is an invalid size");
struct JoyDisableSixAxisDataAccessor {
DataAccessorHeader header{};
std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
};
static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
"JoyDisableSixAxisDataAccessor is an invalid size");
struct JoyEnableSixAxisDataAccessor {
DataAccessorHeader header{};
std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
};
static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
"JoyEnableSixAxisDataAccessor is an invalid size");
struct ButtonOnlyPollingDataAccessor {
DataAccessorHeader header;
std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
};
static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
"ButtonOnlyPollingDataAccessor is an invalid size");
class HidbusBase {
public:
explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
virtual ~HidbusBase();
void ActivateDevice();
void DeactivateDevice();
bool IsDeviceActivated() const;
// Enables/disables the device
void Enable(bool enable);
// returns true if device is enabled
bool IsEnabled() const;
// returns true if polling mode is enabled
bool IsPollingMode() const;
// returns polling mode
JoyPollingMode GetPollingMode() const;
// Sets and enables JoyPollingMode
void SetPollingMode(JoyPollingMode mode);
// Disables JoyPollingMode
void DisablePollingMode();
// Called on EnableJoyPollingReceiveMode
void SetTransferMemoryPointer(u8* t_mem);
Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
virtual void OnInit() {}
virtual void OnRelease() {}
// Updates device transfer memory
virtual void OnUpdate() {}
// Returns the device ID of the joycon
virtual u8 GetDeviceId() const {
return {};
}
// Assigns a command from data
virtual bool SetCommand(const std::vector<u8>& data) {
return {};
}
// Returns a reply from a command
virtual std::vector<u8> GetReply() const {
return {};
}
protected:
bool is_activated{};
bool device_enabled{};
bool polling_mode_enabled{};
JoyPollingMode polling_mode = {};
// TODO(German77): All data accessors need to be replaced with a ring lifo object
JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
ButtonOnlyPollingDataAccessor button_only_data{};
u8* transfer_memory{nullptr};
bool is_transfer_memory_set{};
Kernel::KEvent* send_command_async_event;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID

View File

@@ -1,285 +1,285 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/hidbus/ringcon.h"
namespace Service::HID {
RingController::RingController(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_)
: HidbusBase(service_context_) {
input = hid_core_.GetEmulatedDevices();
}
RingController::~RingController() = default;
void RingController::OnInit() {
return;
}
void RingController::OnRelease() {
return;
};
void RingController::OnUpdate() {
if (!is_activated) {
return;
}
if (!device_enabled) {
return;
}
if (!polling_mode_enabled || !is_transfer_memory_set) {
return;
}
// TODO: Increment multitasking counters from motion and sensor data
switch (polling_mode) {
case JoyPollingMode::SixAxisSensorEnable: {
enable_sixaxis_data.header.total_entries = 10;
enable_sixaxis_data.header.result = ResultSuccess;
const auto& last_entry =
enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
enable_sixaxis_data.header.latest_entry =
(enable_sixaxis_data.header.latest_entry + 1) % 10;
auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
curr_entry.sampling_number = last_entry.sampling_number + 1;
curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
const RingConData ringcon_value = GetSensorValue();
curr_entry.polling_data.out_size = sizeof(ringcon_value);
std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
break;
}
default:
LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
break;
}
}
RingController::RingConData RingController::GetSensorValue() const {
RingConData ringcon_sensor_value{
.status = DataValid::Valid,
.data = 0,
};
const f32 force_value = input->GetRingSensorForce().force * range;
ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
return ringcon_sensor_value;
}
u8 RingController::GetDeviceId() const {
return device_id;
}
std::vector<u8> RingController::GetReply() const {
const RingConCommands current_command = command;
switch (current_command) {
case RingConCommands::GetFirmwareVersion:
return GetFirmwareVersionReply();
case RingConCommands::ReadId:
return GetReadIdReply();
case RingConCommands::c20105:
return GetC020105Reply();
case RingConCommands::ReadUnkCal:
return GetReadUnkCalReply();
case RingConCommands::ReadFactoryCal:
return GetReadFactoryCalReply();
case RingConCommands::ReadUserCal:
return GetReadUserCalReply();
case RingConCommands::ReadRepCount:
return GetReadRepCountReply();
case RingConCommands::ReadTotalPushCount:
return GetReadTotalPushCountReply();
case RingConCommands::ResetRepCount:
return GetResetRepCountReply();
case RingConCommands::SaveCalData:
return GetSaveDataReply();
default:
return GetErrorReply();
}
}
bool RingController::SetCommand(const std::vector<u8>& data) {
if (data.size() < 4) {
LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
command = RingConCommands::Error;
return false;
}
std::memcpy(&command, data.data(), sizeof(RingConCommands));
switch (command) {
case RingConCommands::GetFirmwareVersion:
case RingConCommands::ReadId:
case RingConCommands::c20105:
case RingConCommands::ReadUnkCal:
case RingConCommands::ReadFactoryCal:
case RingConCommands::ReadUserCal:
case RingConCommands::ReadRepCount:
case RingConCommands::ReadTotalPushCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
send_command_async_event->Signal();
return true;
case RingConCommands::ResetRepCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
total_rep_count = 0;
send_command_async_event->Signal();
return true;
case RingConCommands::SaveCalData: {
ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
SaveCalData save_info{};
std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
user_calibration = save_info.calibration;
send_command_async_event->Signal();
return true;
}
default:
LOG_ERROR(Service_HID, "Command not implemented {}", command);
command = RingConCommands::Error;
// Signal a reply to avoid softlocking the game
send_command_async_event->Signal();
return false;
}
}
std::vector<u8> RingController::GetFirmwareVersionReply() const {
const FirmwareVersionReply reply{
.status = DataValid::Valid,
.firmware = version,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadIdReply() const {
// The values are hardcoded from a real joycon
const ReadIdReply reply{
.status = DataValid::Valid,
.id_l_x0 = 8,
.id_l_x0_2 = 41,
.id_l_x4 = 22294,
.id_h_x0 = 19777,
.id_h_x0_2 = 13621,
.id_h_x4 = 8245,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetC020105Reply() const {
const Cmd020105Reply reply{
.status = DataValid::Valid,
.data = 1,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadUnkCalReply() const {
const ReadUnkCalReply reply{
.status = DataValid::Valid,
.data = 0,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadFactoryCalReply() const {
const ReadFactoryCalReply reply{
.status = DataValid::Valid,
.calibration = factory_calibration,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadUserCalReply() const {
const ReadUserCalReply reply{
.status = DataValid::Valid,
.calibration = user_calibration,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadRepCountReply() const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_rep_count, 0, 0},
.crc = GetCrcValue({total_rep_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadTotalPushCountReply() const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_push_count, 0, 0},
.crc = GetCrcValue({total_push_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetResetRepCountReply() const {
return GetReadRepCountReply();
}
std::vector<u8> RingController::GetSaveDataReply() const {
const StatusReply reply{
.status = DataValid::Valid,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetErrorReply() const {
const ErrorReply reply{
.status = DataValid::BadCRC,
};
return GetDataVector(reply);
}
u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
u8 crc = 0;
for (std::size_t index = 0; index < data.size(); index++) {
for (u8 i = 0x80; i > 0; i >>= 1) {
bool bit = (crc & 0x80) != 0;
if ((data[index] & i) != 0) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= 0x8d;
}
}
}
return crc;
}
template <typename T>
std::vector<u8> RingController::GetDataVector(const T& reply) const {
static_assert(std::is_trivially_copyable_v<T>);
std::vector<u8> data;
data.resize(sizeof(reply));
std::memcpy(data.data(), &reply, sizeof(reply));
return data;
}
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/hidbus/ringcon.h"
namespace Service::HID {
RingController::RingController(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_)
: HidbusBase(service_context_) {
input = hid_core_.GetEmulatedDevices();
}
RingController::~RingController() = default;
void RingController::OnInit() {
return;
}
void RingController::OnRelease() {
return;
};
void RingController::OnUpdate() {
if (!is_activated) {
return;
}
if (!device_enabled) {
return;
}
if (!polling_mode_enabled || !is_transfer_memory_set) {
return;
}
// TODO: Increment multitasking counters from motion and sensor data
switch (polling_mode) {
case JoyPollingMode::SixAxisSensorEnable: {
enable_sixaxis_data.header.total_entries = 10;
enable_sixaxis_data.header.result = ResultSuccess;
const auto& last_entry =
enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
enable_sixaxis_data.header.latest_entry =
(enable_sixaxis_data.header.latest_entry + 1) % 10;
auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
curr_entry.sampling_number = last_entry.sampling_number + 1;
curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
const RingConData ringcon_value = GetSensorValue();
curr_entry.polling_data.out_size = sizeof(ringcon_value);
std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
break;
}
default:
LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
break;
}
}
RingController::RingConData RingController::GetSensorValue() const {
RingConData ringcon_sensor_value{
.status = DataValid::Valid,
.data = 0,
};
const f32 force_value = input->GetRingSensorForce().force * range;
ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
return ringcon_sensor_value;
}
u8 RingController::GetDeviceId() const {
return device_id;
}
std::vector<u8> RingController::GetReply() const {
const RingConCommands current_command = command;
switch (current_command) {
case RingConCommands::GetFirmwareVersion:
return GetFirmwareVersionReply();
case RingConCommands::ReadId:
return GetReadIdReply();
case RingConCommands::c20105:
return GetC020105Reply();
case RingConCommands::ReadUnkCal:
return GetReadUnkCalReply();
case RingConCommands::ReadFactoryCal:
return GetReadFactoryCalReply();
case RingConCommands::ReadUserCal:
return GetReadUserCalReply();
case RingConCommands::ReadRepCount:
return GetReadRepCountReply();
case RingConCommands::ReadTotalPushCount:
return GetReadTotalPushCountReply();
case RingConCommands::ResetRepCount:
return GetResetRepCountReply();
case RingConCommands::SaveCalData:
return GetSaveDataReply();
default:
return GetErrorReply();
}
}
bool RingController::SetCommand(const std::vector<u8>& data) {
if (data.size() < 4) {
LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
command = RingConCommands::Error;
return false;
}
std::memcpy(&command, data.data(), sizeof(RingConCommands));
switch (command) {
case RingConCommands::GetFirmwareVersion:
case RingConCommands::ReadId:
case RingConCommands::c20105:
case RingConCommands::ReadUnkCal:
case RingConCommands::ReadFactoryCal:
case RingConCommands::ReadUserCal:
case RingConCommands::ReadRepCount:
case RingConCommands::ReadTotalPushCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
send_command_async_event->Signal();
return true;
case RingConCommands::ResetRepCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
total_rep_count = 0;
send_command_async_event->Signal();
return true;
case RingConCommands::SaveCalData: {
ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
SaveCalData save_info{};
std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
user_calibration = save_info.calibration;
send_command_async_event->Signal();
return true;
}
default:
LOG_ERROR(Service_HID, "Command not implemented {}", command);
command = RingConCommands::Error;
// Signal a reply to avoid softlocking the game
send_command_async_event->Signal();
return false;
}
}
std::vector<u8> RingController::GetFirmwareVersionReply() const {
const FirmwareVersionReply reply{
.status = DataValid::Valid,
.firmware = version,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadIdReply() const {
// The values are hardcoded from a real joycon
const ReadIdReply reply{
.status = DataValid::Valid,
.id_l_x0 = 8,
.id_l_x0_2 = 41,
.id_l_x4 = 22294,
.id_h_x0 = 19777,
.id_h_x0_2 = 13621,
.id_h_x4 = 8245,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetC020105Reply() const {
const Cmd020105Reply reply{
.status = DataValid::Valid,
.data = 1,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadUnkCalReply() const {
const ReadUnkCalReply reply{
.status = DataValid::Valid,
.data = 0,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadFactoryCalReply() const {
const ReadFactoryCalReply reply{
.status = DataValid::Valid,
.calibration = factory_calibration,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadUserCalReply() const {
const ReadUserCalReply reply{
.status = DataValid::Valid,
.calibration = user_calibration,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadRepCountReply() const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_rep_count, 0, 0},
.crc = GetCrcValue({total_rep_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadTotalPushCountReply() const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_push_count, 0, 0},
.crc = GetCrcValue({total_push_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetResetRepCountReply() const {
return GetReadRepCountReply();
}
std::vector<u8> RingController::GetSaveDataReply() const {
const StatusReply reply{
.status = DataValid::Valid,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetErrorReply() const {
const ErrorReply reply{
.status = DataValid::BadCRC,
};
return GetDataVector(reply);
}
u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
u8 crc = 0;
for (std::size_t index = 0; index < data.size(); index++) {
for (u8 i = 0x80; i > 0; i >>= 1) {
bool bit = (crc & 0x80) != 0;
if ((data[index] & i) != 0) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= 0x8d;
}
}
}
return crc;
}
template <typename T>
std::vector<u8> RingController::GetDataVector(const T& reply) const {
static_assert(std::is_trivially_copyable_v<T>);
std::vector<u8> data;
data.resize(sizeof(reply));
std::memcpy(data.data(), &reply, sizeof(reply));
return data;
}
} // namespace Service::HID

View File

@@ -1,253 +1,253 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
namespace Core::HID {
class EmulatedDevices;
} // namespace Core::HID
namespace Service::HID {
class RingController final : public HidbusBase {
public:
explicit RingController(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_);
~RingController() override;
void OnInit() override;
void OnRelease() override;
// Updates ringcon transfer memory
void OnUpdate() override;
// Returns the device ID of the joycon
u8 GetDeviceId() const override;
// Assigns a command from data
bool SetCommand(const std::vector<u8>& data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
private:
// These values are obtained from a real ring controller
static constexpr s16 idle_value = 2280;
static constexpr s16 idle_deadzone = 120;
static constexpr s16 range = 2500;
// Most missing command names are leftovers from other firmware versions
enum class RingConCommands : u32 {
GetFirmwareVersion = 0x00020000,
ReadId = 0x00020100,
JoyPolling = 0x00020101,
Unknown1 = 0x00020104,
c20105 = 0x00020105,
Unknown2 = 0x00020204,
Unknown3 = 0x00020304,
Unknown4 = 0x00020404,
ReadUnkCal = 0x00020504,
ReadFactoryCal = 0x00020A04,
Unknown5 = 0x00021104,
Unknown6 = 0x00021204,
Unknown7 = 0x00021304,
ReadUserCal = 0x00021A04,
ReadRepCount = 0x00023104,
ReadTotalPushCount = 0x00023204,
ResetRepCount = 0x04013104,
Unknown8 = 0x04011104,
Unknown9 = 0x04011204,
Unknown10 = 0x04011304,
SaveCalData = 0x10011A04,
Error = 0xFFFFFFFF,
};
enum class DataValid : u32 {
Valid,
BadCRC,
Cal,
};
struct FirmwareVersion {
u8 sub;
u8 main;
};
static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
struct FactoryCalibration {
s32_le os_max;
s32_le hk_max;
s32_le zero_min;
s32_le zero_max;
};
static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
struct CalibrationValue {
s16 value;
u16 crc;
};
static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
struct UserCalibration {
CalibrationValue os_max;
CalibrationValue hk_max;
CalibrationValue zero;
};
static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
struct SaveCalData {
RingConCommands command;
UserCalibration calibration;
INSERT_PADDING_BYTES_NOINIT(4);
};
static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
static_assert(std::is_trivially_copyable_v<SaveCalData>,
"SaveCalData must be trivially copyable");
struct FirmwareVersionReply {
DataValid status;
FirmwareVersion firmware;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
struct Cmd020105Reply {
DataValid status;
u8 data;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
struct StatusReply {
DataValid status;
};
static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
struct GetThreeByteReply {
DataValid status;
std::array<u8, 3> data;
u8 crc;
};
static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
struct ReadUnkCalReply {
DataValid status;
u16 data;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
struct ReadFactoryCalReply {
DataValid status;
FactoryCalibration calibration;
};
static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
struct ReadUserCalReply {
DataValid status;
UserCalibration calibration;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
struct ReadIdReply {
DataValid status;
u16 id_l_x0;
u16 id_l_x0_2;
u16 id_l_x4;
u16 id_h_x0;
u16 id_h_x0_2;
u16 id_h_x4;
};
static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
struct ErrorReply {
DataValid status;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
struct RingConData {
DataValid status;
s16_le data;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
// Returns RingConData struct with pressure sensor values
RingConData GetSensorValue() const;
// Returns 8 byte reply with firmware version
std::vector<u8> GetFirmwareVersionReply() const;
// Returns 16 byte reply with ID values
std::vector<u8> GetReadIdReply() const;
// (STUBBED) Returns 8 byte reply
std::vector<u8> GetC020105Reply() const;
// (STUBBED) Returns 8 byte empty reply
std::vector<u8> GetReadUnkCalReply() const;
// Returns 20 byte reply with factory calibration values
std::vector<u8> GetReadFactoryCalReply() const;
// Returns 20 byte reply with user calibration values
std::vector<u8> GetReadUserCalReply() const;
// Returns 8 byte reply
std::vector<u8> GetReadRepCountReply() const;
// Returns 8 byte reply
std::vector<u8> GetReadTotalPushCountReply() const;
// Returns 8 byte reply
std::vector<u8> GetResetRepCountReply() const;
// Returns 4 byte save data reply
std::vector<u8> GetSaveDataReply() const;
// Returns 8 byte error reply
std::vector<u8> GetErrorReply() const;
// Returns 8 bit redundancy check from provided data
u8 GetCrcValue(const std::vector<u8>& data) const;
// Converts structs to an u8 vector equivalent
template <typename T>
std::vector<u8> GetDataVector(const T& reply) const;
RingConCommands command{RingConCommands::Error};
// These counters are used in multitasking mode while the switch is sleeping
// Total steps taken
u8 total_rep_count = 0;
// Total times the ring was pushed
u8 total_push_count = 0;
const u8 device_id = 0x20;
const FirmwareVersion version = {
.sub = 0x0,
.main = 0x2c,
};
const FactoryCalibration factory_calibration = {
.os_max = idle_value + range + idle_deadzone,
.hk_max = idle_value - range - idle_deadzone,
.zero_min = idle_value - idle_deadzone,
.zero_max = idle_value + idle_deadzone,
};
UserCalibration user_calibration = {
.os_max = {.value = range, .crc = 228},
.hk_max = {.value = -range, .crc = 239},
.zero = {.value = idle_value, .crc = 225},
};
Core::HID::EmulatedDevices* input;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
namespace Core::HID {
class EmulatedDevices;
} // namespace Core::HID
namespace Service::HID {
class RingController final : public HidbusBase {
public:
explicit RingController(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_);
~RingController() override;
void OnInit() override;
void OnRelease() override;
// Updates ringcon transfer memory
void OnUpdate() override;
// Returns the device ID of the joycon
u8 GetDeviceId() const override;
// Assigns a command from data
bool SetCommand(const std::vector<u8>& data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
private:
// These values are obtained from a real ring controller
static constexpr s16 idle_value = 2280;
static constexpr s16 idle_deadzone = 120;
static constexpr s16 range = 2500;
// Most missing command names are leftovers from other firmware versions
enum class RingConCommands : u32 {
GetFirmwareVersion = 0x00020000,
ReadId = 0x00020100,
JoyPolling = 0x00020101,
Unknown1 = 0x00020104,
c20105 = 0x00020105,
Unknown2 = 0x00020204,
Unknown3 = 0x00020304,
Unknown4 = 0x00020404,
ReadUnkCal = 0x00020504,
ReadFactoryCal = 0x00020A04,
Unknown5 = 0x00021104,
Unknown6 = 0x00021204,
Unknown7 = 0x00021304,
ReadUserCal = 0x00021A04,
ReadRepCount = 0x00023104,
ReadTotalPushCount = 0x00023204,
ResetRepCount = 0x04013104,
Unknown8 = 0x04011104,
Unknown9 = 0x04011204,
Unknown10 = 0x04011304,
SaveCalData = 0x10011A04,
Error = 0xFFFFFFFF,
};
enum class DataValid : u32 {
Valid,
BadCRC,
Cal,
};
struct FirmwareVersion {
u8 sub;
u8 main;
};
static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
struct FactoryCalibration {
s32_le os_max;
s32_le hk_max;
s32_le zero_min;
s32_le zero_max;
};
static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
struct CalibrationValue {
s16 value;
u16 crc;
};
static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
struct UserCalibration {
CalibrationValue os_max;
CalibrationValue hk_max;
CalibrationValue zero;
};
static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
struct SaveCalData {
RingConCommands command;
UserCalibration calibration;
INSERT_PADDING_BYTES_NOINIT(4);
};
static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
static_assert(std::is_trivially_copyable_v<SaveCalData>,
"SaveCalData must be trivially copyable");
struct FirmwareVersionReply {
DataValid status;
FirmwareVersion firmware;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
struct Cmd020105Reply {
DataValid status;
u8 data;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
struct StatusReply {
DataValid status;
};
static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
struct GetThreeByteReply {
DataValid status;
std::array<u8, 3> data;
u8 crc;
};
static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
struct ReadUnkCalReply {
DataValid status;
u16 data;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
struct ReadFactoryCalReply {
DataValid status;
FactoryCalibration calibration;
};
static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
struct ReadUserCalReply {
DataValid status;
UserCalibration calibration;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
struct ReadIdReply {
DataValid status;
u16 id_l_x0;
u16 id_l_x0_2;
u16 id_l_x4;
u16 id_h_x0;
u16 id_h_x0_2;
u16 id_h_x4;
};
static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
struct ErrorReply {
DataValid status;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
struct RingConData {
DataValid status;
s16_le data;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
// Returns RingConData struct with pressure sensor values
RingConData GetSensorValue() const;
// Returns 8 byte reply with firmware version
std::vector<u8> GetFirmwareVersionReply() const;
// Returns 16 byte reply with ID values
std::vector<u8> GetReadIdReply() const;
// (STUBBED) Returns 8 byte reply
std::vector<u8> GetC020105Reply() const;
// (STUBBED) Returns 8 byte empty reply
std::vector<u8> GetReadUnkCalReply() const;
// Returns 20 byte reply with factory calibration values
std::vector<u8> GetReadFactoryCalReply() const;
// Returns 20 byte reply with user calibration values
std::vector<u8> GetReadUserCalReply() const;
// Returns 8 byte reply
std::vector<u8> GetReadRepCountReply() const;
// Returns 8 byte reply
std::vector<u8> GetReadTotalPushCountReply() const;
// Returns 8 byte reply
std::vector<u8> GetResetRepCountReply() const;
// Returns 4 byte save data reply
std::vector<u8> GetSaveDataReply() const;
// Returns 8 byte error reply
std::vector<u8> GetErrorReply() const;
// Returns 8 bit redundancy check from provided data
u8 GetCrcValue(const std::vector<u8>& data) const;
// Converts structs to an u8 vector equivalent
template <typename T>
std::vector<u8> GetDataVector(const T& reply) const;
RingConCommands command{RingConCommands::Error};
// These counters are used in multitasking mode while the switch is sleeping
// Total steps taken
u8 total_rep_count = 0;
// Total times the ring was pushed
u8 total_push_count = 0;
const u8 device_id = 0x20;
const FirmwareVersion version = {
.sub = 0x0,
.main = 0x2c,
};
const FactoryCalibration factory_calibration = {
.os_max = idle_value + range + idle_deadzone,
.hk_max = idle_value - range - idle_deadzone,
.zero_min = idle_value - idle_deadzone,
.zero_max = idle_value + idle_deadzone,
};
UserCalibration user_calibration = {
.os_max = {.value = range, .crc = 228},
.hk_max = {.value = -range, .crc = 239},
.zero = {.value = idle_value, .crc = 225},
};
Core::HID::EmulatedDevices* input;
};
} // namespace Service::HID

View File

@@ -1,50 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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

View File

@@ -1,38 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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

View File

@@ -1,51 +1,51 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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

View File

@@ -1,38 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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

File diff suppressed because it is too large Load Diff

View File

@@ -1,117 +1,117 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
class IRS final : public ServiceFramework<IRS> {
public:
explicit IRS(Core::System& system_);
~IRS() override;
private:
// This is nn::irsensor::detail::AruidFormat
struct AruidFormat {
u64 sensor_aruid;
u64 sensor_aruid_status;
};
static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
// This is nn::irsensor::detail::StatusManager
struct StatusManager {
std::array<Core::IrSensor::DeviceFormat, 9> device;
std::array<AruidFormat, 5> aruid;
};
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void StopImageProcessor(Kernel::HLERequestContext& ctx);
void RunMomentProcessor(Kernel::HLERequestContext& ctx);
void RunClusteringProcessor(Kernel::HLERequestContext& ctx);
void RunImageTransferProcessor(Kernel::HLERequestContext& ctx);
void GetImageTransferProcessorState(Kernel::HLERequestContext& ctx);
void RunTeraPluginProcessor(Kernel::HLERequestContext& ctx);
void GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx);
void RunPointingProcessor(Kernel::HLERequestContext& ctx);
void SuspendImageProcessor(Kernel::HLERequestContext& ctx);
void CheckFirmwareVersion(Kernel::HLERequestContext& ctx);
void SetFunctionLevel(Kernel::HLERequestContext& ctx);
void RunImageTransferExProcessor(Kernel::HLERequestContext& ctx);
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle);
template <typename T>
void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
Core::IrSensor::DeviceFormat& device_state) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return;
}
processors[index] = std::make_unique<T>(device_state);
}
template <typename T>
void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
Core::IrSensor::DeviceFormat& device_state) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return;
}
processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
}
template <typename T>
T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return static_cast<T&>(*processors[0]);
}
return static_cast<T&>(*processors[index]);
}
template <typename T>
const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return static_cast<T&>(*processors[0]);
}
return static_cast<T&>(*processors[index]);
}
Core::HID::EmulatedController* npad_device = nullptr;
StatusManager* shared_memory = nullptr;
std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
public:
explicit IRS_SYS(Core::System& system);
~IRS_SYS() override;
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
class IRS final : public ServiceFramework<IRS> {
public:
explicit IRS(Core::System& system_);
~IRS() override;
private:
// This is nn::irsensor::detail::AruidFormat
struct AruidFormat {
u64 sensor_aruid;
u64 sensor_aruid_status;
};
static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
// This is nn::irsensor::detail::StatusManager
struct StatusManager {
std::array<Core::IrSensor::DeviceFormat, 9> device;
std::array<AruidFormat, 5> aruid;
};
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void StopImageProcessor(Kernel::HLERequestContext& ctx);
void RunMomentProcessor(Kernel::HLERequestContext& ctx);
void RunClusteringProcessor(Kernel::HLERequestContext& ctx);
void RunImageTransferProcessor(Kernel::HLERequestContext& ctx);
void GetImageTransferProcessorState(Kernel::HLERequestContext& ctx);
void RunTeraPluginProcessor(Kernel::HLERequestContext& ctx);
void GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx);
void RunPointingProcessor(Kernel::HLERequestContext& ctx);
void SuspendImageProcessor(Kernel::HLERequestContext& ctx);
void CheckFirmwareVersion(Kernel::HLERequestContext& ctx);
void SetFunctionLevel(Kernel::HLERequestContext& ctx);
void RunImageTransferExProcessor(Kernel::HLERequestContext& ctx);
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle);
template <typename T>
void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
Core::IrSensor::DeviceFormat& device_state) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return;
}
processors[index] = std::make_unique<T>(device_state);
}
template <typename T>
void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
Core::IrSensor::DeviceFormat& device_state) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return;
}
processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
}
template <typename T>
T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return static_cast<T&>(*processors[0]);
}
return static_cast<T&>(*processors[index]);
}
template <typename T>
const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
const auto index = static_cast<std::size_t>(handle.npad_id);
if (index > sizeof(processors)) {
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
return static_cast<T&>(*processors[0]);
}
return static_cast<T&>(*processors[index]);
}
Core::HID::EmulatedController* npad_device = nullptr;
StatusManager* shared_memory = nullptr;
std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
public:
explicit IRS_SYS(Core::System& system);
~IRS_SYS() override;
};
} // namespace Service::IRS

View File

@@ -1,47 +1,47 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
namespace Service::IRS {
template <typename State, std::size_t max_buffer_size>
struct Lifo {
s64 sampling_number{};
s64 buffer_count{};
std::array<State, max_buffer_size> entries{};
const State& ReadCurrentEntry() const {
return entries[GetBufferTail()];
}
const State& ReadPreviousEntry() const {
return entries[GetPreviousEntryIndex()];
}
s64 GetBufferTail() const {
return sampling_number % max_buffer_size;
}
std::size_t GetPreviousEntryIndex() const {
return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
}
std::size_t GetNextEntryIndex() const {
return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
}
void WriteNextEntry(const State& new_state) {
if (buffer_count < static_cast<s64>(max_buffer_size)) {
buffer_count++;
}
sampling_number++;
entries[GetBufferTail()] = new_state;
}
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
namespace Service::IRS {
template <typename State, std::size_t max_buffer_size>
struct Lifo {
s64 sampling_number{};
s64 buffer_count{};
std::array<State, max_buffer_size> entries{};
const State& ReadCurrentEntry() const {
return entries[GetBufferTail()];
}
const State& ReadPreviousEntry() const {
return entries[GetPreviousEntryIndex()];
}
s64 GetBufferTail() const {
return sampling_number % max_buffer_size;
}
std::size_t GetPreviousEntryIndex() const {
return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
}
std::size_t GetNextEntryIndex() const {
return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
}
void WriteNextEntry(const State& new_state) {
if (buffer_count < static_cast<s64>(max_buffer_size)) {
buffer_count++;
}
sampling_number++;
entries[GetBufferTail()] = new_state;
}
};
} // namespace Service::IRS

View File

@@ -1,265 +1,265 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <queue>
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/clustering_processor.h"
namespace Service::IRS {
ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index)
: device{device_format} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
SetDefaultConfig();
shared_memory = std::construct_at(
reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
.is_npad_service = true,
};
callback_key = npad_device->SetCallback(engine_callback);
}
ClusteringProcessor::~ClusteringProcessor() {
npad_device->DeleteCallback(callback_key);
};
void ClusteringProcessor::StartProcessor() {
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
}
void ClusteringProcessor::SuspendProcessor() {}
void ClusteringProcessor::StopProcessor() {}
void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
if (type != Core::HID::ControllerTriggerType::IrSensor) {
return;
}
next_state = {};
const auto camera_data = npad_device->GetCamera();
auto filtered_image = camera_data.data;
RemoveLowIntensityData(filtered_image);
const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
const auto window_end_x =
window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
const auto window_end_y =
window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
for (std::size_t y = window_start_y; y < window_end_y; y++) {
for (std::size_t x = window_start_x; x < window_end_x; x++) {
u8 pixel = GetPixel(filtered_image, x, y);
if (pixel == 0) {
continue;
}
const auto cluster = GetClusterProperties(filtered_image, x, y);
if (cluster.pixel_count > current_config.pixel_count_max) {
continue;
}
if (cluster.pixel_count < current_config.pixel_count_min) {
continue;
}
// Cluster object limit reached
if (next_state.object_count >= next_state.data.size()) {
continue;
}
next_state.data[next_state.object_count] = cluster;
next_state.object_count++;
}
}
next_state.sampling_number = camera_data.sample;
next_state.timestamp = next_state.timestamp + 131;
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
shared_memory->clustering_lifo.WriteNextEntry(next_state);
if (!IsProcessorActive()) {
StartProcessor();
}
}
void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
for (u8& pixel : data) {
if (pixel < current_config.pixel_count_min) {
pixel = 0;
}
}
}
ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
std::size_t x,
std::size_t y) {
using DataPoint = Common::Point<std::size_t>;
std::queue<DataPoint> search_points{};
ClusteringData current_cluster = GetPixelProperties(data, x, y);
SetPixel(data, x, y, 0);
search_points.emplace<DataPoint>({x, y});
while (!search_points.empty()) {
const auto point = search_points.front();
search_points.pop();
// Avoid negative numbers
if (point.x == 0 || point.y == 0) {
continue;
}
std::array<DataPoint, 4> new_points{
DataPoint{point.x - 1, point.y},
{point.x, point.y - 1},
{point.x + 1, point.y},
{point.x, point.y + 1},
};
for (const auto new_point : new_points) {
if (new_point.x >= width) {
continue;
}
if (new_point.y >= height) {
continue;
}
if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
continue;
}
const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
current_cluster = MergeCluster(current_cluster, cluster);
SetPixel(data, new_point.x, new_point.y, 0);
search_points.emplace<DataPoint>({new_point.x, new_point.y});
}
}
return current_cluster;
}
ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
const std::vector<u8>& data, std::size_t x, std::size_t y) const {
return {
.average_intensity = GetPixel(data, x, y) / 255.0f,
.centroid =
{
.x = static_cast<f32>(x),
.y = static_cast<f32>(y),
},
.pixel_count = 1,
.bound =
{
.x = static_cast<s16>(x),
.y = static_cast<s16>(y),
.width = 1,
.height = 1,
},
};
}
ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
const ClusteringData a, const ClusteringData b) const {
const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
const f32 pixel_count = a_pixel_count + b_pixel_count;
const f32 average_intensity =
(a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
const Core::IrSensor::IrsCentroid centroid = {
.x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
.y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
};
s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
s16 a_bound_end_x = a.bound.x + a.bound.width;
s16 a_bound_end_y = a.bound.y + a.bound.height;
s16 b_bound_end_x = b.bound.x + b.bound.width;
s16 b_bound_end_y = b.bound.y + b.bound.height;
const Core::IrSensor::IrsRect bound = {
.x = bound_start_x,
.y = bound_start_y,
.width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
: static_cast<s16>(b_bound_end_x - bound_start_x),
.height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
: static_cast<s16>(b_bound_end_y - bound_start_y),
};
return {
.average_intensity = average_intensity,
.centroid = centroid,
.pixel_count = static_cast<u32>(pixel_count),
.bound = bound,
};
}
u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
if ((y * width) + x > data.size()) {
return 0;
}
return data[(y * width) + x];
}
void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
if ((y * width) + x > data.size()) {
return;
}
data[(y * width) + x] = value;
}
void ClusteringProcessor::SetDefaultConfig() {
using namespace std::literals::chrono_literals;
current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
current_config.camera_config.gain = 2;
current_config.camera_config.is_negative_used = false;
current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
current_config.window_of_interest = {
.x = 0,
.y = 0,
.width = width,
.height = height,
};
current_config.pixel_count_min = 3;
current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
current_config.is_external_light_filter_enabled = true;
current_config.object_intensity_min = 150;
npad_device->SetCameraFormat(format);
}
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.window_of_interest = config.window_of_interest;
current_config.pixel_count_min = config.pixel_count_min;
current_config.pixel_count_max = config.pixel_count_max;
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
current_config.object_intensity_min = config.object_intensity_min;
LOG_INFO(Service_IRS,
"Processor config, exposure_time={}, gain={}, is_negative_used={}, "
"light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
"pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
current_config.camera_config.exposure_time, current_config.camera_config.gain,
current_config.camera_config.is_negative_used,
current_config.camera_config.light_target, current_config.window_of_interest.x,
current_config.window_of_interest.y, current_config.window_of_interest.width,
current_config.window_of_interest.height, current_config.pixel_count_min,
current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
current_config.object_intensity_min);
npad_device->SetCameraFormat(format);
}
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <queue>
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/clustering_processor.h"
namespace Service::IRS {
ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index)
: device{device_format} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
SetDefaultConfig();
shared_memory = std::construct_at(
reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
.is_npad_service = true,
};
callback_key = npad_device->SetCallback(engine_callback);
}
ClusteringProcessor::~ClusteringProcessor() {
npad_device->DeleteCallback(callback_key);
};
void ClusteringProcessor::StartProcessor() {
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
}
void ClusteringProcessor::SuspendProcessor() {}
void ClusteringProcessor::StopProcessor() {}
void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
if (type != Core::HID::ControllerTriggerType::IrSensor) {
return;
}
next_state = {};
const auto camera_data = npad_device->GetCamera();
auto filtered_image = camera_data.data;
RemoveLowIntensityData(filtered_image);
const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
const auto window_end_x =
window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
const auto window_end_y =
window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
for (std::size_t y = window_start_y; y < window_end_y; y++) {
for (std::size_t x = window_start_x; x < window_end_x; x++) {
u8 pixel = GetPixel(filtered_image, x, y);
if (pixel == 0) {
continue;
}
const auto cluster = GetClusterProperties(filtered_image, x, y);
if (cluster.pixel_count > current_config.pixel_count_max) {
continue;
}
if (cluster.pixel_count < current_config.pixel_count_min) {
continue;
}
// Cluster object limit reached
if (next_state.object_count >= next_state.data.size()) {
continue;
}
next_state.data[next_state.object_count] = cluster;
next_state.object_count++;
}
}
next_state.sampling_number = camera_data.sample;
next_state.timestamp = next_state.timestamp + 131;
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
shared_memory->clustering_lifo.WriteNextEntry(next_state);
if (!IsProcessorActive()) {
StartProcessor();
}
}
void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
for (u8& pixel : data) {
if (pixel < current_config.pixel_count_min) {
pixel = 0;
}
}
}
ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
std::size_t x,
std::size_t y) {
using DataPoint = Common::Point<std::size_t>;
std::queue<DataPoint> search_points{};
ClusteringData current_cluster = GetPixelProperties(data, x, y);
SetPixel(data, x, y, 0);
search_points.emplace<DataPoint>({x, y});
while (!search_points.empty()) {
const auto point = search_points.front();
search_points.pop();
// Avoid negative numbers
if (point.x == 0 || point.y == 0) {
continue;
}
std::array<DataPoint, 4> new_points{
DataPoint{point.x - 1, point.y},
{point.x, point.y - 1},
{point.x + 1, point.y},
{point.x, point.y + 1},
};
for (const auto new_point : new_points) {
if (new_point.x >= width) {
continue;
}
if (new_point.y >= height) {
continue;
}
if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
continue;
}
const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
current_cluster = MergeCluster(current_cluster, cluster);
SetPixel(data, new_point.x, new_point.y, 0);
search_points.emplace<DataPoint>({new_point.x, new_point.y});
}
}
return current_cluster;
}
ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
const std::vector<u8>& data, std::size_t x, std::size_t y) const {
return {
.average_intensity = GetPixel(data, x, y) / 255.0f,
.centroid =
{
.x = static_cast<f32>(x),
.y = static_cast<f32>(y),
},
.pixel_count = 1,
.bound =
{
.x = static_cast<s16>(x),
.y = static_cast<s16>(y),
.width = 1,
.height = 1,
},
};
}
ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
const ClusteringData a, const ClusteringData b) const {
const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
const f32 pixel_count = a_pixel_count + b_pixel_count;
const f32 average_intensity =
(a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
const Core::IrSensor::IrsCentroid centroid = {
.x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
.y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
};
s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
s16 a_bound_end_x = a.bound.x + a.bound.width;
s16 a_bound_end_y = a.bound.y + a.bound.height;
s16 b_bound_end_x = b.bound.x + b.bound.width;
s16 b_bound_end_y = b.bound.y + b.bound.height;
const Core::IrSensor::IrsRect bound = {
.x = bound_start_x,
.y = bound_start_y,
.width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
: static_cast<s16>(b_bound_end_x - bound_start_x),
.height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
: static_cast<s16>(b_bound_end_y - bound_start_y),
};
return {
.average_intensity = average_intensity,
.centroid = centroid,
.pixel_count = static_cast<u32>(pixel_count),
.bound = bound,
};
}
u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
if ((y * width) + x > data.size()) {
return 0;
}
return data[(y * width) + x];
}
void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
if ((y * width) + x > data.size()) {
return;
}
data[(y * width) + x] = value;
}
void ClusteringProcessor::SetDefaultConfig() {
using namespace std::literals::chrono_literals;
current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
current_config.camera_config.gain = 2;
current_config.camera_config.is_negative_used = false;
current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
current_config.window_of_interest = {
.x = 0,
.y = 0,
.width = width,
.height = height,
};
current_config.pixel_count_min = 3;
current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
current_config.is_external_light_filter_enabled = true;
current_config.object_intensity_min = 150;
npad_device->SetCameraFormat(format);
}
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.window_of_interest = config.window_of_interest;
current_config.pixel_count_min = config.pixel_count_min;
current_config.pixel_count_max = config.pixel_count_max;
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
current_config.object_intensity_min = config.object_intensity_min;
LOG_INFO(Service_IRS,
"Processor config, exposure_time={}, gain={}, is_negative_used={}, "
"light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
"pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
current_config.camera_config.exposure_time, current_config.camera_config.gain,
current_config.camera_config.is_negative_used,
current_config.camera_config.light_target, current_config.window_of_interest.x,
current_config.window_of_interest.y, current_config.window_of_interest.width,
current_config.window_of_interest.height, current_config.pixel_count_min,
current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
current_config.object_intensity_min);
npad_device->SetCameraFormat(format);
}
} // namespace Service::IRS

View File

@@ -1,110 +1,110 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irs_ring_lifo.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
class ClusteringProcessor final : public ProcessorBase {
public:
explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index);
~ClusteringProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
private:
static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
static constexpr std::size_t width = 320;
static constexpr std::size_t height = 240;
// This is nn::irsensor::ClusteringProcessorConfig
struct ClusteringProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::IrsRect window_of_interest;
u32 pixel_count_min;
u32 pixel_count_max;
u32 object_intensity_min;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
"ClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::AdaptiveClusteringProcessorConfig
struct AdaptiveClusteringProcessorConfig {
Core::IrSensor::AdaptiveClusteringMode mode;
Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
};
static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
"AdaptiveClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::ClusteringData
struct ClusteringData {
f32 average_intensity;
Core::IrSensor::IrsCentroid centroid;
u32 pixel_count;
Core::IrSensor::IrsRect bound;
};
static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
// This is nn::irsensor::ClusteringProcessorState
struct ClusteringProcessorState {
s64 sampling_number;
u64 timestamp;
u8 object_count;
INSERT_PADDING_BYTES(3);
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
std::array<ClusteringData, 0x10> data;
};
static_assert(sizeof(ClusteringProcessorState) == 0x198,
"ClusteringProcessorState is an invalid size");
struct ClusteringSharedMemory {
Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
INSERT_PADDING_WORDS(0x11F);
};
static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
"ClusteringSharedMemory is an invalid size");
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
void RemoveLowIntensityData(std::vector<u8>& data);
ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
std::size_t y) const;
ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
// Sets config parameters of the camera
void SetDefaultConfig();
ClusteringSharedMemory* shared_memory = nullptr;
ClusteringProcessorState next_state{};
ClusteringProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device;
int callback_key{};
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irs_ring_lifo.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
class ClusteringProcessor final : public ProcessorBase {
public:
explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index);
~ClusteringProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
private:
static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
static constexpr std::size_t width = 320;
static constexpr std::size_t height = 240;
// This is nn::irsensor::ClusteringProcessorConfig
struct ClusteringProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::IrsRect window_of_interest;
u32 pixel_count_min;
u32 pixel_count_max;
u32 object_intensity_min;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
"ClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::AdaptiveClusteringProcessorConfig
struct AdaptiveClusteringProcessorConfig {
Core::IrSensor::AdaptiveClusteringMode mode;
Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
};
static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
"AdaptiveClusteringProcessorConfig is an invalid size");
// This is nn::irsensor::ClusteringData
struct ClusteringData {
f32 average_intensity;
Core::IrSensor::IrsCentroid centroid;
u32 pixel_count;
Core::IrSensor::IrsRect bound;
};
static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
// This is nn::irsensor::ClusteringProcessorState
struct ClusteringProcessorState {
s64 sampling_number;
u64 timestamp;
u8 object_count;
INSERT_PADDING_BYTES(3);
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
std::array<ClusteringData, 0x10> data;
};
static_assert(sizeof(ClusteringProcessorState) == 0x198,
"ClusteringProcessorState is an invalid size");
struct ClusteringSharedMemory {
Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
INSERT_PADDING_WORDS(0x11F);
};
static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
"ClusteringSharedMemory is an invalid size");
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
void RemoveLowIntensityData(std::vector<u8>& data);
ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
std::size_t y) const;
ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
// Sets config parameters of the camera
void SetDefaultConfig();
ClusteringSharedMemory* shared_memory = nullptr;
ClusteringProcessorState next_state{};
ClusteringProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device;
int callback_key{};
};
} // namespace Service::IRS

View File

@@ -1,150 +1,150 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
namespace Service::IRS {
ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index)
: device{device_format} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
.is_npad_service = true,
};
callback_key = npad_device->SetCallback(engine_callback);
device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
ImageTransferProcessor::~ImageTransferProcessor() {
npad_device->DeleteCallback(callback_key);
};
void ImageTransferProcessor::StartProcessor() {
is_active = true;
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
processor_state.sampling_number = 0;
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
}
void ImageTransferProcessor::SuspendProcessor() {}
void ImageTransferProcessor::StopProcessor() {}
void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
if (type != Core::HID::ControllerTriggerType::IrSensor) {
return;
}
if (!is_transfer_memory_set) {
return;
}
const auto camera_data = npad_device->GetCamera();
// This indicates how much ambient light is precent
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
processor_state.sampling_number = camera_data.sample;
if (camera_data.format != current_config.origin_format) {
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
current_config.origin_format);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
if (current_config.origin_format > current_config.trimming_format) {
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
current_config.origin_format, current_config.trimming_format);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
std::vector<u8> window_data{};
const auto origin_width = GetDataWidth(current_config.origin_format);
const auto origin_height = GetDataHeight(current_config.origin_format);
const auto trimming_width = GetDataWidth(current_config.trimming_format);
const auto trimming_height = GetDataHeight(current_config.trimming_format);
window_data.resize(GetDataSize(current_config.trimming_format));
if (trimming_width + current_config.trimming_start_x > origin_width ||
trimming_height + current_config.trimming_start_y > origin_height) {
LOG_WARNING(Service_IRS,
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
current_config.trimming_start_x, current_config.trimming_start_y,
trimming_width, trimming_height, origin_width, origin_height);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
for (std::size_t y = 0; y < trimming_height; y++) {
for (std::size_t x = 0; x < trimming_width; x++) {
const std::size_t window_index = (y * trimming_width) + x;
const std::size_t origin_index =
((y + current_config.trimming_start_y) * origin_width) + x +
current_config.trimming_start_x;
window_data[window_index] = camera_data.data[origin_index];
}
}
memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
if (!IsProcessorActive()) {
StartProcessor();
}
}
void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.origin_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
current_config.trimming_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
current_config.trimming_start_x = 0;
current_config.trimming_start_y = 0;
npad_device->SetCameraFormat(current_config.origin_format);
}
void ImageTransferProcessor::SetConfig(
Core::IrSensor::PackedImageTransferProcessorExConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.origin_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
current_config.trimming_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
current_config.trimming_start_x = config.trimming_start_x;
current_config.trimming_start_y = config.trimming_start_y;
npad_device->SetCameraFormat(current_config.origin_format);
}
void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
is_transfer_memory_set = true;
transfer_memory = t_mem;
}
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const {
const auto size = GetDataSize(current_config.trimming_format);
data.resize(size);
memcpy(data.data(), transfer_memory, size);
return processor_state;
}
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
namespace Service::IRS {
ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index)
: device{device_format} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
.is_npad_service = true,
};
callback_key = npad_device->SetCallback(engine_callback);
device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
ImageTransferProcessor::~ImageTransferProcessor() {
npad_device->DeleteCallback(callback_key);
};
void ImageTransferProcessor::StartProcessor() {
is_active = true;
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
processor_state.sampling_number = 0;
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
}
void ImageTransferProcessor::SuspendProcessor() {}
void ImageTransferProcessor::StopProcessor() {}
void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
if (type != Core::HID::ControllerTriggerType::IrSensor) {
return;
}
if (!is_transfer_memory_set) {
return;
}
const auto camera_data = npad_device->GetCamera();
// This indicates how much ambient light is precent
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
processor_state.sampling_number = camera_data.sample;
if (camera_data.format != current_config.origin_format) {
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
current_config.origin_format);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
if (current_config.origin_format > current_config.trimming_format) {
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
current_config.origin_format, current_config.trimming_format);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
std::vector<u8> window_data{};
const auto origin_width = GetDataWidth(current_config.origin_format);
const auto origin_height = GetDataHeight(current_config.origin_format);
const auto trimming_width = GetDataWidth(current_config.trimming_format);
const auto trimming_height = GetDataHeight(current_config.trimming_format);
window_data.resize(GetDataSize(current_config.trimming_format));
if (trimming_width + current_config.trimming_start_x > origin_width ||
trimming_height + current_config.trimming_start_y > origin_height) {
LOG_WARNING(Service_IRS,
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
current_config.trimming_start_x, current_config.trimming_start_y,
trimming_width, trimming_height, origin_width, origin_height);
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
return;
}
for (std::size_t y = 0; y < trimming_height; y++) {
for (std::size_t x = 0; x < trimming_width; x++) {
const std::size_t window_index = (y * trimming_width) + x;
const std::size_t origin_index =
((y + current_config.trimming_start_y) * origin_width) + x +
current_config.trimming_start_x;
window_data[window_index] = camera_data.data[origin_index];
}
}
memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
if (!IsProcessorActive()) {
StartProcessor();
}
}
void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.origin_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
current_config.trimming_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
current_config.trimming_start_x = 0;
current_config.trimming_start_y = 0;
npad_device->SetCameraFormat(current_config.origin_format);
}
void ImageTransferProcessor::SetConfig(
Core::IrSensor::PackedImageTransferProcessorExConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.origin_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
current_config.trimming_format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
current_config.trimming_start_x = config.trimming_start_x;
current_config.trimming_start_y = config.trimming_start_y;
npad_device->SetCameraFormat(current_config.origin_format);
}
void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
is_transfer_memory_set = true;
transfer_memory = t_mem;
}
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const {
const auto size = GetDataSize(current_config.trimming_format);
data.resize(size);
memcpy(data.data(), transfer_memory, size);
return processor_state;
}
} // namespace Service::IRS

View File

@@ -1,73 +1,73 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
class ImageTransferProcessor final : public ProcessorBase {
public:
explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index);
~ImageTransferProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
// Transfer memory where the image data will be stored
void SetTransferMemoryPointer(u8* t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
private:
// This is nn::irsensor::ImageTransferProcessorConfig
struct ImageTransferProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::ImageTransferProcessorFormat format;
};
static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
"ImageTransferProcessorConfig is an invalid size");
// This is nn::irsensor::ImageTransferProcessorExConfig
struct ImageTransferProcessorExConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::ImageTransferProcessorFormat origin_format;
Core::IrSensor::ImageTransferProcessorFormat trimming_format;
u16 trimming_start_x;
u16 trimming_start_y;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
"ImageTransferProcessorExConfig is an invalid size");
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
ImageTransferProcessorExConfig current_config{};
Core::IrSensor::ImageTransferProcessorState processor_state{};
Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device;
int callback_key{};
u8* transfer_memory = nullptr;
bool is_transfer_memory_set = false;
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS {
class ImageTransferProcessor final : public ProcessorBase {
public:
explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index);
~ImageTransferProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
// Transfer memory where the image data will be stored
void SetTransferMemoryPointer(u8* t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
private:
// This is nn::irsensor::ImageTransferProcessorConfig
struct ImageTransferProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::ImageTransferProcessorFormat format;
};
static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
"ImageTransferProcessorConfig is an invalid size");
// This is nn::irsensor::ImageTransferProcessorExConfig
struct ImageTransferProcessorExConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::ImageTransferProcessorFormat origin_format;
Core::IrSensor::ImageTransferProcessorFormat trimming_format;
u16 trimming_start_x;
u16 trimming_start_y;
bool is_external_light_filter_enabled;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
"ImageTransferProcessorExConfig is an invalid size");
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
ImageTransferProcessorExConfig current_config{};
Core::IrSensor::ImageTransferProcessorState processor_state{};
Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device;
int callback_key{};
u8* transfer_memory = nullptr;
bool is_transfer_memory_set = false;
};
} // namespace Service::IRS

View File

@@ -1,27 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
namespace Service::IRS {
IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
IrLedProcessor::~IrLedProcessor() = default;
void IrLedProcessor::StartProcessor() {}
void IrLedProcessor::SuspendProcessor() {}
void IrLedProcessor::StopProcessor() {}
void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
current_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
}
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
namespace Service::IRS {
IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
IrLedProcessor::~IrLedProcessor() = default;
void IrLedProcessor::StartProcessor() {}
void IrLedProcessor::SuspendProcessor() {}
void IrLedProcessor::StopProcessor() {}
void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
current_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
}
} // namespace Service::IRS

View File

@@ -1,47 +1,47 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class IrLedProcessor final : public ProcessorBase {
public:
explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
~IrLedProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
private:
// This is nn::irsensor::IrLedProcessorConfig
struct IrLedProcessorConfig {
Core::IrSensor::CameraLightTarget light_target;
};
static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
struct IrLedProcessorState {
s64 sampling_number;
u64 timestamp;
std::array<u8, 0x8> data;
};
static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
IrLedProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class IrLedProcessor final : public ProcessorBase {
public:
explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
~IrLedProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
private:
// This is nn::irsensor::IrLedProcessorConfig
struct IrLedProcessorConfig {
Core::IrSensor::CameraLightTarget light_target;
};
static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
struct IrLedProcessorState {
s64 sampling_number;
u64 timestamp;
std::array<u8, 0x8> data;
};
static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
IrLedProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,34 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/moment_processor.h"
namespace Service::IRS {
MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
MomentProcessor::~MomentProcessor() = default;
void MomentProcessor::StartProcessor() {}
void MomentProcessor::SuspendProcessor() {}
void MomentProcessor::StopProcessor() {}
void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.window_of_interest = config.window_of_interest;
current_config.preprocess =
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
}
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/moment_processor.h"
namespace Service::IRS {
MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
MomentProcessor::~MomentProcessor() = default;
void MomentProcessor::StartProcessor() {}
void MomentProcessor::SuspendProcessor() {}
void MomentProcessor::StopProcessor() {}
void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
current_config.window_of_interest = config.window_of_interest;
current_config.preprocess =
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
}
} // namespace Service::IRS

View File

@@ -1,61 +1,61 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class MomentProcessor final : public ProcessorBase {
public:
explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
~MomentProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
private:
// This is nn::irsensor::MomentProcessorConfig
struct MomentProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::IrsRect window_of_interest;
Core::IrSensor::MomentProcessorPreprocess preprocess;
u32 preprocess_intensity_threshold;
};
static_assert(sizeof(MomentProcessorConfig) == 0x28,
"MomentProcessorConfig is an invalid size");
// This is nn::irsensor::MomentStatistic
struct MomentStatistic {
f32 average_intensity;
Core::IrSensor::IrsCentroid centroid;
};
static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
// This is nn::irsensor::MomentProcessorState
struct MomentProcessorState {
s64 sampling_number;
u64 timestamp;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
INSERT_PADDING_BYTES(4);
std::array<MomentStatistic, 0x30> stadistic;
};
static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
MomentProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class MomentProcessor final : public ProcessorBase {
public:
explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
~MomentProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
private:
// This is nn::irsensor::MomentProcessorConfig
struct MomentProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
Core::IrSensor::IrsRect window_of_interest;
Core::IrSensor::MomentProcessorPreprocess preprocess;
u32 preprocess_intensity_threshold;
};
static_assert(sizeof(MomentProcessorConfig) == 0x28,
"MomentProcessorConfig is an invalid size");
// This is nn::irsensor::MomentStatistic
struct MomentStatistic {
f32 average_intensity;
Core::IrSensor::IrsCentroid centroid;
};
static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
// This is nn::irsensor::MomentProcessorState
struct MomentProcessorState {
s64 sampling_number;
u64 timestamp;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
INSERT_PADDING_BYTES(4);
std::array<MomentStatistic, 0x30> stadistic;
};
static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
MomentProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,26 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/pointing_processor.h"
namespace Service::IRS {
PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
PointingProcessor::~PointingProcessor() = default;
void PointingProcessor::StartProcessor() {}
void PointingProcessor::SuspendProcessor() {}
void PointingProcessor::StopProcessor() {}
void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
current_config.window_of_interest = config.window_of_interest;
}
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/pointing_processor.h"
namespace Service::IRS {
PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
PointingProcessor::~PointingProcessor() = default;
void PointingProcessor::StartProcessor() {}
void PointingProcessor::SuspendProcessor() {}
void PointingProcessor::StopProcessor() {}
void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
current_config.window_of_interest = config.window_of_interest;
}
} // namespace Service::IRS

View File

@@ -1,61 +1,61 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class PointingProcessor final : public ProcessorBase {
public:
explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
~PointingProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
private:
// This is nn::irsensor::PointingProcessorConfig
struct PointingProcessorConfig {
Core::IrSensor::IrsRect window_of_interest;
};
static_assert(sizeof(PointingProcessorConfig) == 0x8,
"PointingProcessorConfig is an invalid size");
struct PointingProcessorMarkerData {
u8 pointing_status;
INSERT_PADDING_BYTES(3);
u32 unknown;
float unknown_float1;
float position_x;
float position_y;
float unknown_float2;
Core::IrSensor::IrsRect window_of_interest;
};
static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
"PointingProcessorMarkerData is an invalid size");
struct PointingProcessorMarkerState {
s64 sampling_number;
u64 timestamp;
std::array<PointingProcessorMarkerData, 0x3> data;
};
static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
"PointingProcessorMarkerState is an invalid size");
PointingProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class PointingProcessor final : public ProcessorBase {
public:
explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
~PointingProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
private:
// This is nn::irsensor::PointingProcessorConfig
struct PointingProcessorConfig {
Core::IrSensor::IrsRect window_of_interest;
};
static_assert(sizeof(PointingProcessorConfig) == 0x8,
"PointingProcessorConfig is an invalid size");
struct PointingProcessorMarkerData {
u8 pointing_status;
INSERT_PADDING_BYTES(3);
u32 unknown;
float unknown_float1;
float position_x;
float position_y;
float unknown_float2;
Core::IrSensor::IrsRect window_of_interest;
};
static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
"PointingProcessorMarkerData is an invalid size");
struct PointingProcessorMarkerState {
s64 sampling_number;
u64 timestamp;
std::array<PointingProcessorMarkerData, 0x3> data;
};
static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
"PointingProcessorMarkerState is an invalid size");
PointingProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,67 +1,67 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
ProcessorBase::ProcessorBase() {}
ProcessorBase::~ProcessorBase() = default;
bool ProcessorBase::IsProcessorActive() const {
return is_active;
}
std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 320 * 240;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 160 * 120;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 80 * 60;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 40 * 30;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 20 * 15;
default:
return 0;
}
}
std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 320;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 160;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 80;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 40;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 20;
default:
return 0;
}
}
std::size_t ProcessorBase::GetDataHeight(
Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 240;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 120;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 60;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 30;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 15;
default:
return 0;
}
}
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
ProcessorBase::ProcessorBase() {}
ProcessorBase::~ProcessorBase() = default;
bool ProcessorBase::IsProcessorActive() const {
return is_active;
}
std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 320 * 240;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 160 * 120;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 80 * 60;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 40 * 30;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 20 * 15;
default:
return 0;
}
}
std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 320;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 160;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 80;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 40;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 20;
default:
return 0;
}
}
std::size_t ProcessorBase::GetDataHeight(
Core::IrSensor::ImageTransferProcessorFormat format) const {
switch (format) {
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
return 240;
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
return 120;
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
return 60;
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
return 30;
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
return 15;
default:
return 0;
}
}
} // namespace Service::IRS

View File

@@ -1,33 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
namespace Service::IRS {
class ProcessorBase {
public:
explicit ProcessorBase();
virtual ~ProcessorBase();
virtual void StartProcessor() = 0;
virtual void SuspendProcessor() = 0;
virtual void StopProcessor() = 0;
bool IsProcessorActive() const;
protected:
/// Returns the number of bytes the image uses
std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
/// Returns the width of the image
std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
/// Returns the height of the image
std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
bool is_active{false};
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/irs_types.h"
namespace Service::IRS {
class ProcessorBase {
public:
explicit ProcessorBase();
virtual ~ProcessorBase();
virtual void StartProcessor() = 0;
virtual void SuspendProcessor() = 0;
virtual void StopProcessor() = 0;
bool IsProcessorActive() const;
protected:
/// Returns the number of bytes the image uses
std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
/// Returns the width of the image
std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
/// Returns the height of the image
std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
bool is_active{false};
};
} // namespace Service::IRS

View File

@@ -1,29 +1,29 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
namespace Service::IRS {
TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
TeraPluginProcessor::~TeraPluginProcessor() = default;
void TeraPluginProcessor::StartProcessor() {}
void TeraPluginProcessor::SuspendProcessor() {}
void TeraPluginProcessor::StopProcessor() {}
void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
current_config.mode = config.mode;
current_config.unknown_1 = config.unknown_1;
current_config.unknown_2 = config.unknown_2;
current_config.unknown_3 = config.unknown_3;
}
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
namespace Service::IRS {
TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
: device(device_format) {
device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
}
TeraPluginProcessor::~TeraPluginProcessor() = default;
void TeraPluginProcessor::StartProcessor() {}
void TeraPluginProcessor::SuspendProcessor() {}
void TeraPluginProcessor::StopProcessor() {}
void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
current_config.mode = config.mode;
current_config.unknown_1 = config.unknown_1;
current_config.unknown_2 = config.unknown_2;
current_config.unknown_3 = config.unknown_3;
}
} // namespace Service::IRS

View File

@@ -1,53 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class TeraPluginProcessor final : public ProcessorBase {
public:
explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
~TeraPluginProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
private:
// This is nn::irsensor::TeraPluginProcessorConfig
struct TeraPluginProcessorConfig {
u8 mode;
u8 unknown_1;
u8 unknown_2;
u8 unknown_3;
};
static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
"TeraPluginProcessorConfig is an invalid size");
struct TeraPluginProcessorState {
s64 sampling_number;
u64 timestamp;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
std::array<u8, 0x12c> data;
};
static_assert(sizeof(TeraPluginProcessorState) == 0x140,
"TeraPluginProcessorState is an invalid size");
TeraPluginProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
namespace Service::IRS {
class TeraPluginProcessor final : public ProcessorBase {
public:
explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
~TeraPluginProcessor() override;
// Called when the processor is initialized
void StartProcessor() override;
// Called when the processor is suspended
void SuspendProcessor() override;
// Called when the processor is stopped
void StopProcessor() override;
// Sets config parameters of the camera
void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
private:
// This is nn::irsensor::TeraPluginProcessorConfig
struct TeraPluginProcessorConfig {
u8 mode;
u8 unknown_1;
u8 unknown_2;
u8 unknown_3;
};
static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
"TeraPluginProcessorConfig is an invalid size");
struct TeraPluginProcessorState {
s64 sampling_number;
u64 timestamp;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
std::array<u8, 0x12c> data;
};
static_assert(sizeof(TeraPluginProcessorState) == 0x140,
"TeraPluginProcessorState is an invalid size");
TeraPluginProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
};
} // namespace Service::IRS

View File

@@ -1,53 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
namespace Service::HID {
template <typename State>
struct AtomicStorage {
s64 sampling_number;
State state;
};
template <typename State, std::size_t max_buffer_size>
struct Lifo {
s64 timestamp{};
s64 total_buffer_count = static_cast<s64>(max_buffer_size);
s64 buffer_tail{};
s64 buffer_count{};
std::array<AtomicStorage<State>, max_buffer_size> entries{};
const AtomicStorage<State>& ReadCurrentEntry() const {
return entries[buffer_tail];
}
const AtomicStorage<State>& ReadPreviousEntry() const {
return entries[GetPreviousEntryIndex()];
}
std::size_t GetPreviousEntryIndex() const {
return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count);
}
std::size_t GetNextEntryIndex() const {
return static_cast<size_t>((buffer_tail + 1) % total_buffer_count);
}
void WriteNextEntry(const State& new_state) {
if (buffer_count < total_buffer_count - 1) {
buffer_count++;
}
buffer_tail = GetNextEntryIndex();
const auto& previous_entry = ReadPreviousEntry();
entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
entries[buffer_tail].state = new_state;
}
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
namespace Service::HID {
template <typename State>
struct AtomicStorage {
s64 sampling_number;
State state;
};
template <typename State, std::size_t max_buffer_size>
struct Lifo {
s64 timestamp{};
s64 total_buffer_count = static_cast<s64>(max_buffer_size);
s64 buffer_tail{};
s64 buffer_count{};
std::array<AtomicStorage<State>, max_buffer_size> entries{};
const AtomicStorage<State>& ReadCurrentEntry() const {
return entries[buffer_tail];
}
const AtomicStorage<State>& ReadPreviousEntry() const {
return entries[GetPreviousEntryIndex()];
}
std::size_t GetPreviousEntryIndex() const {
return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count);
}
std::size_t GetNextEntryIndex() const {
return static_cast<size_t>((buffer_tail + 1) % total_buffer_count);
}
void WriteNextEntry(const State& new_state) {
if (buffer_count < total_buffer_count - 1) {
buffer_count++;
}
buffer_tail = GetNextEntryIndex();
const auto& previous_entry = ReadPreviousEntry();
entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
entries[buffer_tail].state = new_state;
}
};
} // namespace Service::HID

View File

@@ -1,40 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/xcd.h"
namespace Service::HID {
XCD_SYS::XCD_SYS(Core::System& system_) : ServiceFramework{system_, "xcd:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetDataFormat"},
{1, nullptr, "SetDataFormat"},
{2, nullptr, "GetMcuState"},
{3, nullptr, "SetMcuState"},
{4, nullptr, "GetMcuVersionForNfc"},
{5, nullptr, "CheckNfcDevicePower"},
{10, nullptr, "SetNfcEvent"},
{11, nullptr, "GetNfcInfo"},
{12, nullptr, "StartNfcDiscovery"},
{13, nullptr, "StopNfcDiscovery"},
{14, nullptr, "StartNtagRead"},
{15, nullptr, "StartNtagWrite"},
{16, nullptr, "SendNfcRawData"},
{17, nullptr, "RegisterMifareKey"},
{18, nullptr, "ClearMifareKey"},
{19, nullptr, "StartMifareRead"},
{20, nullptr, "StartMifareWrite"},
{101, nullptr, "GetAwakeTriggerReasonForLeftRail"},
{102, nullptr, "GetAwakeTriggerReasonForRightRail"},
{103, nullptr, "GetAwakeTriggerBatteryLevelTransitionForLeftRail"},
{104, nullptr, "GetAwakeTriggerBatteryLevelTransitionForRightRail"},
};
// clang-format on
RegisterHandlers(functions);
}
XCD_SYS::~XCD_SYS() = default;
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/xcd.h"
namespace Service::HID {
XCD_SYS::XCD_SYS(Core::System& system_) : ServiceFramework{system_, "xcd:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetDataFormat"},
{1, nullptr, "SetDataFormat"},
{2, nullptr, "GetMcuState"},
{3, nullptr, "SetMcuState"},
{4, nullptr, "GetMcuVersionForNfc"},
{5, nullptr, "CheckNfcDevicePower"},
{10, nullptr, "SetNfcEvent"},
{11, nullptr, "GetNfcInfo"},
{12, nullptr, "StartNfcDiscovery"},
{13, nullptr, "StopNfcDiscovery"},
{14, nullptr, "StartNtagRead"},
{15, nullptr, "StartNtagWrite"},
{16, nullptr, "SendNfcRawData"},
{17, nullptr, "RegisterMifareKey"},
{18, nullptr, "ClearMifareKey"},
{19, nullptr, "StartMifareRead"},
{20, nullptr, "StartMifareWrite"},
{101, nullptr, "GetAwakeTriggerReasonForLeftRail"},
{102, nullptr, "GetAwakeTriggerReasonForRightRail"},
{103, nullptr, "GetAwakeTriggerBatteryLevelTransitionForLeftRail"},
{104, nullptr, "GetAwakeTriggerBatteryLevelTransitionForRightRail"},
};
// clang-format on
RegisterHandlers(functions);
}
XCD_SYS::~XCD_SYS() = default;
} // namespace Service::HID

View File

@@ -1,20 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::HID {
class XCD_SYS final : public ServiceFramework<XCD_SYS> {
public:
explicit XCD_SYS(Core::System& system_);
~XCD_SYS() override;
};
} // namespace Service::HID
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::HID {
class XCD_SYS final : public ServiceFramework<XCD_SYS> {
public:
explicit XCD_SYS(Core::System& system_);
~XCD_SYS() override;
};
} // namespace Service::HID