summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando S2021-10-23 23:28:22 +0200
committerGravatar GitHub2021-10-23 23:28:22 +0200
commit7461196839cdc090f2c547cccb645fb112ec592c (patch)
treed5ff026a915ac9d53a1076082ecf91446ee21254 /src
parentMerge pull request #7207 from ameerj/vs-2022 (diff)
parentinput_common: Fix data race on GC implementation (diff)
downloadyuzu-7461196839cdc090f2c547cccb645fb112ec592c.tar.gz
yuzu-7461196839cdc090f2c547cccb645fb112ec592c.tar.xz
yuzu-7461196839cdc090f2c547cccb645fb112ec592c.zip
Merge pull request #6515 from german77/gc_thread_safe
input_common: Fix data race on GC implementation
Diffstat (limited to 'src')
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp189
-rw-r--r--src/input_common/gcadapter/gc_adapter.h46
2 files changed, 120 insertions, 115 deletions
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index a2f1bb67c..3c3b6fbde 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -14,15 +14,73 @@
14 14
15namespace GCAdapter { 15namespace GCAdapter {
16 16
17class LibUSBContext {
18public:
19 explicit LibUSBContext() {
20 init_result = libusb_init(&ctx);
21 }
22
23 ~LibUSBContext() {
24 libusb_exit(ctx);
25 }
26
27 LibUSBContext& operator=(const LibUSBContext&) = delete;
28 LibUSBContext(const LibUSBContext&) = delete;
29
30 LibUSBContext& operator=(LibUSBContext&&) noexcept = delete;
31 LibUSBContext(LibUSBContext&&) noexcept = delete;
32
33 [[nodiscard]] int InitResult() const noexcept {
34 return init_result;
35 }
36
37 [[nodiscard]] libusb_context* get() noexcept {
38 return ctx;
39 }
40
41private:
42 libusb_context* ctx;
43 int init_result{};
44};
45
46class LibUSBDeviceHandle {
47public:
48 explicit LibUSBDeviceHandle(libusb_context* ctx, uint16_t vid, uint16_t pid) noexcept {
49 handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
50 }
51
52 ~LibUSBDeviceHandle() noexcept {
53 if (handle) {
54 libusb_release_interface(handle, 1);
55 libusb_close(handle);
56 }
57 }
58
59 LibUSBDeviceHandle& operator=(const LibUSBDeviceHandle&) = delete;
60 LibUSBDeviceHandle(const LibUSBDeviceHandle&) = delete;
61
62 LibUSBDeviceHandle& operator=(LibUSBDeviceHandle&&) noexcept = delete;
63 LibUSBDeviceHandle(LibUSBDeviceHandle&&) noexcept = delete;
64
65 [[nodiscard]] libusb_device_handle* get() noexcept {
66 return handle;
67 }
68
69private:
70 libusb_device_handle* handle{};
71};
72
17Adapter::Adapter() { 73Adapter::Adapter() {
18 if (usb_adapter_handle != nullptr) { 74 if (usb_adapter_handle) {
19 return; 75 return;
20 } 76 }
21 LOG_INFO(Input, "GC Adapter Initialization started"); 77 LOG_INFO(Input, "GC Adapter Initialization started");
22 78
23 const int init_res = libusb_init(&libusb_ctx); 79 libusb_ctx = std::make_unique<LibUSBContext>();
80 const int init_res = libusb_ctx->InitResult();
24 if (init_res == LIBUSB_SUCCESS) { 81 if (init_res == LIBUSB_SUCCESS) {
25 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 82 adapter_scan_thread =
83 std::jthread([this](std::stop_token stop_token) { AdapterScanThread(stop_token); });
26 } else { 84 } else {
27 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 85 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
28 } 86 }
@@ -32,17 +90,15 @@ Adapter::~Adapter() {
32 Reset(); 90 Reset();
33} 91}
34 92
35void Adapter::AdapterInputThread() { 93void Adapter::AdapterInputThread(std::stop_token stop_token) {
36 LOG_DEBUG(Input, "GC Adapter input thread started"); 94 LOG_DEBUG(Input, "GC Adapter input thread started");
37 s32 payload_size{}; 95 s32 payload_size{};
38 AdapterPayload adapter_payload{}; 96 AdapterPayload adapter_payload{};
39 97
40 if (adapter_scan_thread.joinable()) { 98 adapter_scan_thread = {};
41 adapter_scan_thread.join();
42 }
43 99
44 while (adapter_input_thread_running) { 100 while (!stop_token.stop_requested()) {
45 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), 101 libusb_interrupt_transfer(usb_adapter_handle->get(), input_endpoint, adapter_payload.data(),
46 static_cast<s32>(adapter_payload.size()), &payload_size, 16); 102 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
47 if (IsPayloadCorrect(adapter_payload, payload_size)) { 103 if (IsPayloadCorrect(adapter_payload, payload_size)) {
48 UpdateControllers(adapter_payload); 104 UpdateControllers(adapter_payload);
@@ -52,7 +108,8 @@ void Adapter::AdapterInputThread() {
52 } 108 }
53 109
54 if (restart_scan_thread) { 110 if (restart_scan_thread) {
55 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 111 adapter_scan_thread =
112 std::jthread([this](std::stop_token token) { AdapterScanThread(token); });
56 restart_scan_thread = false; 113 restart_scan_thread = false;
57 } 114 }
58} 115}
@@ -64,7 +121,7 @@ bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payloa
64 adapter_payload[0]); 121 adapter_payload[0]);
65 if (input_error_counter++ > 20) { 122 if (input_error_counter++ > 20) {
66 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?"); 123 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
67 adapter_input_thread_running = false; 124 adapter_input_thread.request_stop();
68 restart_scan_thread = true; 125 restart_scan_thread = true;
69 } 126 }
70 return false; 127 return false;
@@ -96,7 +153,7 @@ void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
96 return; 153 return;
97 } 154 }
98 // Device changed reset device and set new type 155 // Device changed reset device and set new type
99 ResetDevice(port); 156 pads[port] = {};
100 pads[port].type = pad_type; 157 pads[port].type = pad_type;
101} 158}
102 159
@@ -213,8 +270,9 @@ void Adapter::SendVibrations() {
213 const u8 p3 = pads[2].enable_vibration; 270 const u8 p3 = pads[2].enable_vibration;
214 const u8 p4 = pads[3].enable_vibration; 271 const u8 p4 = pads[3].enable_vibration;
215 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4}; 272 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
216 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(), 273 const int err =
217 static_cast<s32>(payload.size()), &size, 16); 274 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(),
275 static_cast<s32>(payload.size()), &size, 16);
218 if (err) { 276 if (err) {
219 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err)); 277 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
220 if (output_error_counter++ > 5) { 278 if (output_error_counter++ > 5) {
@@ -233,56 +291,53 @@ bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
233 return rumble_enabled; 291 return rumble_enabled;
234} 292}
235 293
236void Adapter::AdapterScanThread() { 294void Adapter::AdapterScanThread(std::stop_token stop_token) {
237 adapter_scan_thread_running = true; 295 usb_adapter_handle = nullptr;
238 adapter_input_thread_running = false; 296 pads = {};
239 if (adapter_input_thread.joinable()) { 297 while (!stop_token.stop_requested() && !Setup()) {
240 adapter_input_thread.join(); 298 std::this_thread::sleep_for(std::chrono::seconds(2));
241 }
242 ClearLibusbHandle();
243 ResetDevices();
244 while (adapter_scan_thread_running && !adapter_input_thread_running) {
245 Setup();
246 std::this_thread::sleep_for(std::chrono::seconds(1));
247 } 299 }
248} 300}
249 301
250void Adapter::Setup() { 302bool Adapter::Setup() {
251 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337); 303 constexpr u16 nintendo_vid = 0x057e;
252 304 constexpr u16 gc_adapter_pid = 0x0337;
253 if (usb_adapter_handle == NULL) { 305 usb_adapter_handle =
254 return; 306 std::make_unique<LibUSBDeviceHandle>(libusb_ctx->get(), nintendo_vid, gc_adapter_pid);
307 if (!usb_adapter_handle->get()) {
308 return false;
255 } 309 }
256 if (!CheckDeviceAccess()) { 310 if (!CheckDeviceAccess()) {
257 ClearLibusbHandle(); 311 usb_adapter_handle = nullptr;
258 return; 312 return false;
259 } 313 }
260 314
261 libusb_device* device = libusb_get_device(usb_adapter_handle); 315 libusb_device* const device = libusb_get_device(usb_adapter_handle->get());
262 316
263 LOG_INFO(Input, "GC adapter is now connected"); 317 LOG_INFO(Input, "GC adapter is now connected");
264 // GC Adapter found and accessible, registering it 318 // GC Adapter found and accessible, registering it
265 if (GetGCEndpoint(device)) { 319 if (GetGCEndpoint(device)) {
266 adapter_scan_thread_running = false;
267 adapter_input_thread_running = true;
268 rumble_enabled = true; 320 rumble_enabled = true;
269 input_error_counter = 0; 321 input_error_counter = 0;
270 output_error_counter = 0; 322 output_error_counter = 0;
271 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this); 323 adapter_input_thread =
324 std::jthread([this](std::stop_token stop_token) { AdapterInputThread(stop_token); });
325 return true;
272 } 326 }
327 return false;
273} 328}
274 329
275bool Adapter::CheckDeviceAccess() { 330bool Adapter::CheckDeviceAccess() {
276 // This fixes payload problems from offbrand GCAdapters 331 // This fixes payload problems from offbrand GCAdapters
277 const s32 control_transfer_error = 332 const s32 control_transfer_error =
278 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000); 333 libusb_control_transfer(usb_adapter_handle->get(), 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
279 if (control_transfer_error < 0) { 334 if (control_transfer_error < 0) {
280 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error); 335 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
281 } 336 }
282 337
283 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 338 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle->get(), 0);
284 if (kernel_driver_error == 1) { 339 if (kernel_driver_error == 1) {
285 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 340 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle->get(), 0);
286 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 341 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
287 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", 342 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
288 kernel_driver_error); 343 kernel_driver_error);
@@ -290,15 +345,13 @@ bool Adapter::CheckDeviceAccess() {
290 } 345 }
291 346
292 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 347 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
293 libusb_close(usb_adapter_handle);
294 usb_adapter_handle = nullptr; 348 usb_adapter_handle = nullptr;
295 return false; 349 return false;
296 } 350 }
297 351
298 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); 352 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle->get(), 0);
299 if (interface_claim_error) { 353 if (interface_claim_error) {
300 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); 354 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
301 libusb_close(usb_adapter_handle);
302 usb_adapter_handle = nullptr; 355 usb_adapter_handle = nullptr;
303 return false; 356 return false;
304 } 357 }
@@ -332,57 +385,17 @@ bool Adapter::GetGCEndpoint(libusb_device* device) {
332 // This transfer seems to be responsible for clearing the state of the adapter 385 // This transfer seems to be responsible for clearing the state of the adapter
333 // Used to clear the "busy" state of when the device is unexpectedly unplugged 386 // Used to clear the "busy" state of when the device is unexpectedly unplugged
334 unsigned char clear_payload = 0x13; 387 unsigned char clear_payload = 0x13;
335 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 388 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, &clear_payload,
336 sizeof(clear_payload), nullptr, 16); 389 sizeof(clear_payload), nullptr, 16);
337 return true; 390 return true;
338} 391}
339 392
340void Adapter::JoinThreads() {
341 restart_scan_thread = false;
342 adapter_input_thread_running = false;
343 adapter_scan_thread_running = false;
344
345 if (adapter_scan_thread.joinable()) {
346 adapter_scan_thread.join();
347 }
348
349 if (adapter_input_thread.joinable()) {
350 adapter_input_thread.join();
351 }
352}
353
354void Adapter::ClearLibusbHandle() {
355 if (usb_adapter_handle) {
356 libusb_release_interface(usb_adapter_handle, 1);
357 libusb_close(usb_adapter_handle);
358 usb_adapter_handle = nullptr;
359 }
360}
361
362void Adapter::ResetDevices() {
363 for (std::size_t i = 0; i < pads.size(); ++i) {
364 ResetDevice(i);
365 }
366}
367
368void Adapter::ResetDevice(std::size_t port) {
369 pads[port].type = ControllerTypes::None;
370 pads[port].enable_vibration = false;
371 pads[port].rumble_amplitude = 0;
372 pads[port].buttons = 0;
373 pads[port].last_button = PadButton::Undefined;
374 pads[port].axis_values.fill(0);
375 pads[port].reset_origin_counter = 0;
376}
377
378void Adapter::Reset() { 393void Adapter::Reset() {
379 JoinThreads(); 394 adapter_scan_thread = {};
380 ClearLibusbHandle(); 395 adapter_input_thread = {};
381 ResetDevices(); 396 usb_adapter_handle = nullptr;
382 397 pads = {};
383 if (libusb_ctx) { 398 libusb_ctx = nullptr;
384 libusb_exit(libusb_ctx);
385 }
386} 399}
387 400
388std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 401std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index e5de5e94f..28dbcbe05 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -3,11 +3,14 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6
6#include <algorithm> 7#include <algorithm>
7#include <functional> 8#include <functional>
8#include <mutex> 9#include <mutex>
10#include <stop_token>
9#include <thread> 11#include <thread>
10#include <unordered_map> 12#include <unordered_map>
13
11#include "common/common_types.h" 14#include "common/common_types.h"
12#include "common/threadsafe_queue.h" 15#include "common/threadsafe_queue.h"
13#include "input_common/main.h" 16#include "input_common/main.h"
@@ -18,6 +21,9 @@ struct libusb_device_handle;
18 21
19namespace GCAdapter { 22namespace GCAdapter {
20 23
24class LibUSBContext;
25class LibUSBDeviceHandle;
26
21enum class PadButton { 27enum class PadButton {
22 Undefined = 0x0000, 28 Undefined = 0x0000,
23 ButtonLeft = 0x0001, 29 ButtonLeft = 0x0001,
@@ -63,11 +69,11 @@ struct GCPadStatus {
63}; 69};
64 70
65struct GCController { 71struct GCController {
66 ControllerTypes type{}; 72 ControllerTypes type = ControllerTypes::None;
67 bool enable_vibration{}; 73 bool enable_vibration = false;
68 u8 rumble_amplitude{}; 74 u8 rumble_amplitude = 0;
69 u16 buttons{}; 75 u16 buttons = 0;
70 PadButton last_button{}; 76 PadButton last_button = PadButton::Undefined;
71 std::array<s16, 6> axis_values{}; 77 std::array<s16, 6> axis_values{};
72 std::array<u8, 6> axis_origin{}; 78 std::array<u8, 6> axis_origin{};
73 u8 reset_origin_counter{}; 79 u8 reset_origin_counter{};
@@ -109,9 +115,9 @@ private:
109 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload); 115 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
110 void UpdateVibrations(); 116 void UpdateVibrations();
111 117
112 void AdapterInputThread(); 118 void AdapterInputThread(std::stop_token stop_token);
113 119
114 void AdapterScanThread(); 120 void AdapterScanThread(std::stop_token stop_token);
115 121
116 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size); 122 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
117 123
@@ -119,13 +125,7 @@ private:
119 void SendVibrations(); 125 void SendVibrations();
120 126
121 /// For use in initialization, querying devices to find the adapter 127 /// For use in initialization, querying devices to find the adapter
122 void Setup(); 128 bool Setup();
123
124 /// Resets status of all GC controller devices to a disconnected state
125 void ResetDevices();
126
127 /// Resets status of device connected to a disconnected state
128 void ResetDevice(std::size_t port);
129 129
130 /// Returns true if we successfully gain access to GC Adapter 130 /// Returns true if we successfully gain access to GC Adapter
131 bool CheckDeviceAccess(); 131 bool CheckDeviceAccess();
@@ -137,23 +137,15 @@ private:
137 /// For shutting down, clear all data, join all threads, release usb 137 /// For shutting down, clear all data, join all threads, release usb
138 void Reset(); 138 void Reset();
139 139
140 // Join all threads 140 std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
141 void JoinThreads();
142
143 // Release usb handles
144 void ClearLibusbHandle();
145
146 libusb_device_handle* usb_adapter_handle = nullptr;
147 std::array<GCController, 4> pads; 141 std::array<GCController, 4> pads;
148 Common::SPSCQueue<GCPadStatus> pad_queue; 142 Common::SPSCQueue<GCPadStatus> pad_queue;
149 143
150 std::thread adapter_input_thread; 144 std::jthread adapter_input_thread;
151 std::thread adapter_scan_thread; 145 std::jthread adapter_scan_thread;
152 bool adapter_input_thread_running; 146 bool restart_scan_thread{};
153 bool adapter_scan_thread_running;
154 bool restart_scan_thread;
155 147
156 libusb_context* libusb_ctx; 148 std::unique_ptr<LibUSBContext> libusb_ctx;
157 149
158 u8 input_endpoint{0}; 150 u8 input_endpoint{0};
159 u8 output_endpoint{0}; 151 u8 output_endpoint{0};