aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xREADME.md2
-rwxr-xr-xsrc/input_common/gcadapter/gc_adapter.cpp185
-rwxr-xr-xsrc/input_common/gcadapter/gc_adapter.h47
3 files changed, 120 insertions, 114 deletions
diff --git a/README.md b/README.md
index 47b6a59fe..f00da1969 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
1yuzu emulator early access 1yuzu emulator early access
2============= 2=============
3 3
4This is the source code for early-access 1816. 4This is the source code for early-access 1817.
5 5
6## Legal Notice 6## Legal Notice
7 7
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 320f51ee6..672ea9d74 100755
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -21,15 +21,73 @@
21 21
22namespace GCAdapter { 22namespace GCAdapter {
23 23
24class LibUSBContext {
25public:
26 explicit LibUSBContext() {
27 init_result = libusb_init(&ctx);
28 }
29
30 ~LibUSBContext() {
31 libusb_exit(ctx);
32 }
33
34 LibUSBContext& operator=(const LibUSBContext&) = delete;
35 LibUSBContext(const LibUSBContext&) = delete;
36
37 LibUSBContext& operator=(LibUSBContext&&) noexcept = delete;
38 LibUSBContext(LibUSBContext&&) noexcept = delete;
39
40 [[nodiscard]] int InitResult() const noexcept {
41 return init_result;
42 }
43
44 [[nodiscard]] libusb_context* get() noexcept {
45 return ctx;
46 }
47
48private:
49 libusb_context* ctx;
50 int init_result{};
51};
52
53class LibUSBDeviceHandle {
54public:
55 explicit LibUSBDeviceHandle(libusb_context* ctx, uint16_t vid, uint16_t pid) noexcept {
56 handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
57 }
58
59 ~LibUSBDeviceHandle() noexcept {
60 if (handle) {
61 libusb_release_interface(handle, 1);
62 libusb_close(handle);
63 }
64 }
65
66 LibUSBDeviceHandle& operator=(const LibUSBDeviceHandle&) = delete;
67 LibUSBDeviceHandle(const LibUSBDeviceHandle&) = delete;
68
69 LibUSBDeviceHandle& operator=(LibUSBDeviceHandle&&) noexcept = delete;
70 LibUSBDeviceHandle(LibUSBDeviceHandle&&) noexcept = delete;
71
72 [[nodiscard]] libusb_device_handle* get() noexcept {
73 return handle;
74 }
75
76private:
77 libusb_device_handle* handle{};
78};
79
24Adapter::Adapter() { 80Adapter::Adapter() {
25 if (usb_adapter_handle != nullptr) { 81 if (usb_adapter_handle) {
26 return; 82 return;
27 } 83 }
28 LOG_INFO(Input, "GC Adapter Initialization started"); 84 LOG_INFO(Input, "GC Adapter Initialization started");
29 85
30 const int init_res = libusb_init(&libusb_ctx); 86 libusb_ctx = std::make_unique<LibUSBContext>();
87 const int init_res = libusb_ctx->InitResult();
31 if (init_res == LIBUSB_SUCCESS) { 88 if (init_res == LIBUSB_SUCCESS) {
32 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 89 adapter_scan_thread =
90 std::jthread([this](std::stop_token stop_token) { AdapterScanThread(stop_token); });
33 } else { 91 } else {
34 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 92 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
35 } 93 }
@@ -39,17 +97,16 @@ Adapter::~Adapter() {
39 Reset(); 97 Reset();
40} 98}
41 99
42void Adapter::AdapterInputThread() { 100void Adapter::AdapterInputThread(std::stop_token stop_token) {
43 LOG_DEBUG(Input, "GC Adapter input thread started"); 101 LOG_DEBUG(Input, "GC Adapter input thread started");
44 s32 payload_size{}; 102 s32 payload_size{};
45 AdapterPayload adapter_payload{}; 103 AdapterPayload adapter_payload{};
46 104
47 if (adapter_scan_thread.joinable()) { 105 adapter_scan_thread = {};
48 adapter_scan_thread.join(); 106 adapter_input_thread_running = true;
49 }
50 107
51 while (adapter_input_thread_running) { 108 while (adapter_input_thread_running && !stop_token.stop_requested()) {
52 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), 109 libusb_interrupt_transfer(usb_adapter_handle->get(), input_endpoint, adapter_payload.data(),
53 static_cast<s32>(adapter_payload.size()), &payload_size, 16); 110 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
54 if (IsPayloadCorrect(adapter_payload, payload_size)) { 111 if (IsPayloadCorrect(adapter_payload, payload_size)) {
55 UpdateControllers(adapter_payload); 112 UpdateControllers(adapter_payload);
@@ -59,7 +116,8 @@ void Adapter::AdapterInputThread() {
59 } 116 }
60 117
61 if (restart_scan_thread) { 118 if (restart_scan_thread) {
62 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 119 adapter_scan_thread =
120 std::jthread([this](std::stop_token token) { AdapterScanThread(token); });
63 restart_scan_thread = false; 121 restart_scan_thread = false;
64 } 122 }
65} 123}
@@ -103,7 +161,7 @@ void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
103 return; 161 return;
104 } 162 }
105 // Device changed reset device and set new type 163 // Device changed reset device and set new type
106 ResetDevice(port); 164 pads[port] = {};
107 pads[port].type = pad_type; 165 pads[port].type = pad_type;
108} 166}
109 167
@@ -220,8 +278,9 @@ void Adapter::SendVibrations() {
220 const u8 p3 = pads[2].enable_vibration; 278 const u8 p3 = pads[2].enable_vibration;
221 const u8 p4 = pads[3].enable_vibration; 279 const u8 p4 = pads[3].enable_vibration;
222 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4}; 280 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
223 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(), 281 const int err =
224 static_cast<s32>(payload.size()), &size, 16); 282 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(),
283 static_cast<s32>(payload.size()), &size, 16);
225 if (err) { 284 if (err) {
226 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err)); 285 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
227 if (output_error_counter++ > 5) { 286 if (output_error_counter++ > 5) {
@@ -240,56 +299,52 @@ bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
240 return rumble_enabled; 299 return rumble_enabled;
241} 300}
242 301
243void Adapter::AdapterScanThread() { 302void Adapter::AdapterScanThread(std::stop_token stop_token) {
244 adapter_scan_thread_running = true; 303 usb_adapter_handle = nullptr;
245 adapter_input_thread_running = false; 304 pads = {};
246 if (adapter_input_thread.joinable()) { 305 while (!stop_token.stop_requested() && !Setup()) {
247 adapter_input_thread.join();
248 }
249 ClearLibusbHandle();
250 ResetDevices();
251 while (adapter_scan_thread_running && !adapter_input_thread_running) {
252 Setup();
253 std::this_thread::sleep_for(std::chrono::seconds(1)); 306 std::this_thread::sleep_for(std::chrono::seconds(1));
254 } 307 }
255} 308}
256 309
257void Adapter::Setup() { 310bool Adapter::Setup() {
258 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337); 311 constexpr u16 vid = 0x057e; // Nintendo
259 312 constexpr u16 pid = 0x0337; // GC Adapter
260 if (usb_adapter_handle == NULL) { 313 usb_adapter_handle = std::make_unique<LibUSBDeviceHandle>(libusb_ctx->get(), vid, pid);
261 return; 314 if (!usb_adapter_handle->get()) {
315 return false;
262 } 316 }
263 if (!CheckDeviceAccess()) { 317 if (!CheckDeviceAccess()) {
264 ClearLibusbHandle(); 318 usb_adapter_handle = nullptr;
265 return; 319 return false;
266 } 320 }
267 321
268 libusb_device* device = libusb_get_device(usb_adapter_handle); 322 libusb_device* const device = libusb_get_device(usb_adapter_handle->get());
269 323
270 LOG_INFO(Input, "GC adapter is now connected"); 324 LOG_INFO(Input, "GC adapter is now connected");
271 // GC Adapter found and accessible, registering it 325 // GC Adapter found and accessible, registering it
272 if (GetGCEndpoint(device)) { 326 if (GetGCEndpoint(device)) {
273 adapter_scan_thread_running = false;
274 adapter_input_thread_running = true;
275 rumble_enabled = true; 327 rumble_enabled = true;
276 input_error_counter = 0; 328 input_error_counter = 0;
277 output_error_counter = 0; 329 output_error_counter = 0;
278 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this); 330 adapter_input_thread =
331 std::jthread([this](std::stop_token stop_token) { AdapterInputThread(stop_token); });
332 return true;
279 } 333 }
334 return false;
280} 335}
281 336
282bool Adapter::CheckDeviceAccess() { 337bool Adapter::CheckDeviceAccess() {
283 // This fixes payload problems from offbrand GCAdapters 338 // This fixes payload problems from offbrand GCAdapters
284 const s32 control_transfer_error = 339 const s32 control_transfer_error =
285 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000); 340 libusb_control_transfer(usb_adapter_handle->get(), 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
286 if (control_transfer_error < 0) { 341 if (control_transfer_error < 0) {
287 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error); 342 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
288 } 343 }
289 344
290 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 345 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle->get(), 0);
291 if (kernel_driver_error == 1) { 346 if (kernel_driver_error == 1) {
292 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 347 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle->get(), 0);
293 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 348 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
294 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", 349 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
295 kernel_driver_error); 350 kernel_driver_error);
@@ -297,15 +352,13 @@ bool Adapter::CheckDeviceAccess() {
297 } 352 }
298 353
299 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 354 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
300 libusb_close(usb_adapter_handle);
301 usb_adapter_handle = nullptr; 355 usb_adapter_handle = nullptr;
302 return false; 356 return false;
303 } 357 }
304 358
305 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); 359 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle->get(), 0);
306 if (interface_claim_error) { 360 if (interface_claim_error) {
307 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); 361 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
308 libusb_close(usb_adapter_handle);
309 usb_adapter_handle = nullptr; 362 usb_adapter_handle = nullptr;
310 return false; 363 return false;
311 } 364 }
@@ -339,57 +392,17 @@ bool Adapter::GetGCEndpoint(libusb_device* device) {
339 // This transfer seems to be responsible for clearing the state of the adapter 392 // This transfer seems to be responsible for clearing the state of the adapter
340 // Used to clear the "busy" state of when the device is unexpectedly unplugged 393 // Used to clear the "busy" state of when the device is unexpectedly unplugged
341 unsigned char clear_payload = 0x13; 394 unsigned char clear_payload = 0x13;
342 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 395 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, &clear_payload,
343 sizeof(clear_payload), nullptr, 16); 396 sizeof(clear_payload), nullptr, 16);
344 return true; 397 return true;
345} 398}
346 399
347void Adapter::JoinThreads() {
348 restart_scan_thread = false;
349 adapter_input_thread_running = false;
350 adapter_scan_thread_running = false;
351
352 if (adapter_scan_thread.joinable()) {
353 adapter_scan_thread.join();
354 }
355
356 if (adapter_input_thread.joinable()) {
357 adapter_input_thread.join();
358 }
359}
360
361void Adapter::ClearLibusbHandle() {
362 if (usb_adapter_handle) {
363 libusb_release_interface(usb_adapter_handle, 1);
364 libusb_close(usb_adapter_handle);
365 usb_adapter_handle = nullptr;
366 }
367}
368
369void Adapter::ResetDevices() {
370 for (std::size_t i = 0; i < pads.size(); ++i) {
371 ResetDevice(i);
372 }
373}
374
375void Adapter::ResetDevice(std::size_t port) {
376 pads[port].type = ControllerTypes::None;
377 pads[port].enable_vibration = false;
378 pads[port].rumble_amplitude = 0;
379 pads[port].buttons = 0;
380 pads[port].last_button = PadButton::Undefined;
381 pads[port].axis_values.fill(0);
382 pads[port].reset_origin_counter = 0;
383}
384
385void Adapter::Reset() { 400void Adapter::Reset() {
386 JoinThreads(); 401 adapter_scan_thread = {};
387 ClearLibusbHandle(); 402 adapter_input_thread = {};
388 ResetDevices(); 403 usb_adapter_handle = nullptr;
389 404 pads = {};
390 if (libusb_ctx) { 405 libusb_ctx = nullptr;
391 libusb_exit(libusb_ctx);
392 }
393} 406}
394 407
395std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 408std::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..4a1c87490 100755
--- 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};
@@ -164,5 +156,6 @@ private:
164 bool configuring{false}; 156 bool configuring{false};
165 bool rumble_enabled{true}; 157 bool rumble_enabled{true};
166 bool vibration_changed{true}; 158 bool vibration_changed{true};
159 bool adapter_input_thread_running{false};
167}; 160};
168} // namespace GCAdapter 161} // namespace GCAdapter