Compare commits

..

2 Commits

Author SHA1 Message Date
lizzie 2a14f37ed8 2026-09-11 20:06:05
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-11 20:06:05 +00:00
lizzie 5fcfa46ca8 2026-09-11 18:26:30
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-11 18:26:30 +00:00
5 changed files with 120 additions and 54 deletions
@@ -298,7 +298,7 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
return NvResult::Success;
}
static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList(
static std::vector<Tegra::CommandHeader> BuildWaitCommandList(
NvFence fence) {
return {
Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
@@ -310,9 +310,9 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitComman
};
}
static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementCommandList(
static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(
NvFence fence) {
boost::container::small_vector<Tegra::CommandHeader, 512> result{
std::vector<Tegra::CommandHeader> result{
Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
Tegra::SubmissionMode::Increasing),
{}};
@@ -327,9 +327,9 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementC
return result;
}
static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementWithWfiCommandList(
static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(
NvFence fence) {
boost::container::small_vector<Tegra::CommandHeader, 512> result{
std::vector<Tegra::CommandHeader> result{
Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,
Tegra::SubmissionMode::Increasing),
{}};
+106 -39
View File
@@ -28,10 +28,8 @@ JoyconDriver::~JoyconDriver() {
}
void JoyconDriver::Stop() {
if (input_thread.joinable()) {
input_thread.request_stop();
input_thread.join();
}
is_connected = false;
input_thread = {};
}
Common::Input::DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
@@ -60,6 +58,7 @@ Common::Input::DriverResult JoyconDriver::InitializeDevice() {
return Common::Input::DriverResult::InvalidHandle;
}
std::scoped_lock lock{mutex};
disable_input_thread = true;
// Reset Counters
error_counter = 0;
@@ -127,44 +126,62 @@ Common::Input::DriverResult JoyconDriver::InitializeDevice() {
right_stick_calibration, motion_calibration);
// Start polling for data
if (!input_thread.joinable()) {
input_thread = std::jthread([this](std::stop_token stop_token) {
InputThread(stop_token);
});
is_connected = true;
if (!input_thread_running) {
input_thread =
std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
}
disable_input_thread = false;
return Common::Input::DriverResult::Success;
}
void JoyconDriver::InputThread(std::stop_token stop_token) {
LOG_INFO(Input, "Joycon Adapter input thread started");
Common::SetCurrentThreadName("JoyconInput");
input_thread_running = true;
// Max update rate is 5ms, ensure we are always able to read a bit faster
constexpr int ThreadDelay = 3;
std::vector<u8> buffer(MaxBufferSize);
while (!stop_token.stop_requested()) {
// Max update rate is 5ms, so just (timeout) at 300ms
constexpr int READ_TIMEOUT_MS = 300;
constexpr size_t MAX_VIBRATIONS = 4;
std::array<u8, MaxBufferSize> buffer; // Filled by SDL, don't zero-init
int status = 0;
if (IsInputThreadValid()) {
// By disabling the input thread we can ensure custom commands will succeed as no package is
// skipped
status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(), READ_TIMEOUT_MS);
if (IsPayloadCorrect(status, buffer)) {
OnNewData(buffer);
}
if (!vibration_queue.Empty()) {
VibrationValue vibration_value;
vibration_queue.Pop(vibration_value);
last_vibration_result = rumble_protocol->SendVibration(vibration_value);
}
// We can't keep up with vibrations. Start skipping.
while (vibration_queue.Size() >= MAX_VIBRATIONS) {
vibration_queue.Pop();
}
} else {
if (!IsInputThreadValid()) {
input_thread.request_stop();
continue;
}
// By disabling the input thread we can ensure custom commands will succeed as no package is
// skipped
if (!disable_input_thread) {
status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
ThreadDelay);
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
}
if (IsPayloadCorrect(status, buffer)) {
OnNewData(buffer);
}
if (!vibration_queue.Empty()) {
VibrationValue vibration_value;
vibration_queue.Pop(vibration_value);
last_vibration_result = rumble_protocol->SendVibration(vibration_value);
}
// We can't keep up with vibrations. Start skipping.
while (vibration_queue.Size() > 6) {
vibration_queue.Pop();
}
std::this_thread::yield();
}
is_connected = false;
input_thread_running = false;
LOG_INFO(Input, "Joycon Adapter input thread stopped");
}
@@ -254,6 +271,11 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
}
Common::Input::DriverResult JoyconDriver::SetPollingMode() {
SCOPE_EXIT {
disable_input_thread = false;
};
disable_input_thread = true;
rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
if (motion_enabled && supported_features.motion) {
@@ -360,12 +382,17 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
}
bool JoyconDriver::IsInputThreadValid() const {
if (hidapi_handle == nullptr || hidapi_handle->handle == nullptr)
if (!is_connected.load()) {
return false;
}
if (hidapi_handle->handle == nullptr) {
return false;
}
// Controller is not responding. Terminate connection
if (error_counter > MaxErrorCount)
if (error_counter > MaxErrorCount) {
return false;
return input_thread.joinable();
}
return true;
}
bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
@@ -388,18 +415,30 @@ bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
std::scoped_lock lock{mutex};
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
vibration_queue.Push(vibration);
return last_vibration_result;
}
Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
std::scoped_lock lock{mutex};
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
return generic_protocol->SetLedPattern(led_pattern);
}
Common::Input::DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
std::scoped_lock lock{mutex};
return irs_protocol->SetIrsConfig(mode_, format_);
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
disable_input_thread = true;
const auto result = irs_protocol->SetIrsConfig(mode_, format_);
disable_input_thread = false;
return result;
}
Common::Input::DriverResult JoyconDriver::SetPassiveMode() {
@@ -493,7 +532,12 @@ Common::Input::DriverResult JoyconDriver::StartNfcPolling() {
if (!nfc_protocol->IsEnabled()) {
return Common::Input::DriverResult::Disabled;
}
return nfc_protocol->StartNFCPollingMode();
disable_input_thread = true;
const auto result = nfc_protocol->StartNFCPollingMode();
disable_input_thread = false;
return result;
}
Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
@@ -506,7 +550,10 @@ Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
return Common::Input::DriverResult::Disabled;
}
disable_input_thread = true;
const auto result = nfc_protocol->StopNFCPollingMode();
disable_input_thread = false;
if (amiibo_detected) {
amiibo_detected = false;
joycon_poller->UpdateAmiibo({});
@@ -529,7 +576,11 @@ Common::Input::DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_da
}
out_data.resize(0x21C);
return nfc_protocol->ReadAmiibo(out_data);
disable_input_thread = true;
const auto result = nfc_protocol->ReadAmiibo(out_data);
disable_input_thread = false;
return result;
}
Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
@@ -544,7 +595,12 @@ Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data)
if (!amiibo_detected) {
return Common::Input::DriverResult::ErrorWritingData;
}
return nfc_protocol->WriteAmiibo(data);
disable_input_thread = true;
const auto result = nfc_protocol->WriteAmiibo(data);
disable_input_thread = false;
return result;
}
Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
@@ -560,7 +616,12 @@ Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareR
if (!amiibo_detected) {
return Common::Input::DriverResult::ErrorWritingData;
}
return nfc_protocol->ReadMifare(data, out_data);
disable_input_thread = true;
const auto result = nfc_protocol->ReadMifare(data, out_data);
disable_input_thread = false;
return result;
}
Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
@@ -575,11 +636,17 @@ Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const Mifare
if (!amiibo_detected) {
return Common::Input::DriverResult::ErrorWritingData;
}
return nfc_protocol->WriteMifare(data);
disable_input_thread = true;
const auto result = nfc_protocol->WriteMifare(data);
disable_input_thread = false;
return result;
}
bool JoyconDriver::IsConnected() const {
return input_thread.joinable();
std::scoped_lock lock{mutex};
return is_connected.load();
}
bool JoyconDriver::IsVibrationEnabled() const {
+2 -3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -163,6 +160,8 @@ private:
// Thread related
mutable std::mutex mutex;
std::jthread input_thread;
bool input_thread_running{};
bool disable_input_thread{};
};
} // namespace InputCommon::Joycon
@@ -22,7 +22,7 @@ void JoyconPoller::SetCallbacks(const JoyconCallbacks& callbacks_) {
void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
const RingStatus& ring_status) {
InputReportActive data{};
std::memcpy(&data, buffer.data(), sizeof(InputReportActive));
memcpy(&data, buffer.data(), sizeof(InputReportActive));
switch (device_type) {
case ControllerType::Left:
@@ -47,7 +47,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& moti
void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
InputReportPassive data{};
std::memcpy(&data, buffer.data(), sizeof(InputReportPassive));
memcpy(&data, buffer.data(), sizeof(InputReportPassive));
switch (device_type) {
case ControllerType::Left:
+5 -5
View File
@@ -109,11 +109,11 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub
struct CommandList final {
CommandList() = default;
explicit CommandList(std::size_t size) : command_lists(size) {}
explicit CommandList(boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
: prefetch_command_list{std::move(prefetch_command_list_)} {}
boost::container::small_vector<CommandListHeader, 512> command_lists;
boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
explicit CommandList(std::vector<CommandHeader>&& prefetch_command_list_)
: prefetch_command_list{std::move(prefetch_command_list_)}
{}
std::vector<CommandListHeader> command_lists;
std::vector<CommandHeader> prefetch_command_list;
};
/// @brief The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the