diff options
Diffstat (limited to 'src/input_common/gcadapter/gc_adapter.cpp')
-rwxr-xr-x | src/input_common/gcadapter/gc_adapter.cpp | 185 |
1 files changed, 99 insertions, 86 deletions
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 | ||
22 | namespace GCAdapter { | 22 | namespace GCAdapter { |
23 | 23 | ||
24 | class LibUSBContext { | ||
25 | public: | ||
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 | |||
48 | private: | ||
49 | libusb_context* ctx; | ||
50 | int init_result{}; | ||
51 | }; | ||
52 | |||
53 | class LibUSBDeviceHandle { | ||
54 | public: | ||
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 | |||
76 | private: | ||
77 | libusb_device_handle* handle{}; | ||
78 | }; | ||
79 | |||
24 | Adapter::Adapter() { | 80 | Adapter::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 | ||
42 | void Adapter::AdapterInputThread() { | 100 | void 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 | ||
243 | void Adapter::AdapterScanThread() { | 302 | void 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 | ||
257 | void Adapter::Setup() { | 310 | bool 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 | ||
282 | bool Adapter::CheckDeviceAccess() { | 337 | bool 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 | ||
347 | void 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 | |||
361 | void 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 | |||
369 | void Adapter::ResetDevices() { | ||
370 | for (std::size_t i = 0; i < pads.size(); ++i) { | ||
371 | ResetDevice(i); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | void 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 | |||
385 | void Adapter::Reset() { | 400 | void 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 | ||
395 | std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { | 408 | std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { |