From 8e556603afa0d58bbde9433b3c3095c384025a57 Mon Sep 17 00:00:00 2001 From: mathieui Date: Mon, 26 Jan 2015 13:54:54 +0100 Subject: [PATCH] Fix issues with the GC adapter handling code If we successfully detach the kernel driver from the interface, we should continue instead of aborting the setup. And we should not use libusb_handle_events(), as the API says it is only for backwards compatibility. Additionally, if the adapter thread is not active, dolphin will take 60 seconds to close because the libusb_handle_events() timeout is hardcoded to 60 seconds. Instead, use libusb_handle_events_timeout_completed() with a timeout of 1 second. Also, cancel the libusb transfers before the join(), to be able to close the usb device without libusb screaming in the background (and potentially crashing). And finally, split the Init() and Shutdown() functions to avoid having to init and exit libusb every time we neeed to detect the adapter. --- Source/Core/Core/HW/SI_GCAdapter.cpp | 255 +++++++++++++++------------ Source/Core/Core/HW/SI_GCAdapter.h | 5 + 2 files changed, 150 insertions(+), 110 deletions(-) diff --git a/Source/Core/Core/HW/SI_GCAdapter.cpp b/Source/Core/Core/HW/SI_GCAdapter.cpp index 655d76b068..c1ff3a4932 100644 --- a/Source/Core/Core/HW/SI_GCAdapter.cpp +++ b/Source/Core/Core/HW/SI_GCAdapter.cpp @@ -55,9 +55,10 @@ extern "C" static void HandleEvents() { + timeval tv = {1, 0}; while (s_adapter_thread_running.IsSet()) { - libusb_handle_events(NULL); + libusb_handle_events_timeout_completed(s_libusb_context, &tv, NULL); Common::YieldCPU(); } } @@ -92,12 +93,6 @@ void Init() s_libusb_driver_not_supported = false; - for (int i = 0; i < MAX_SI_CHANNELS; i++) - { - s_controller_type[i] = CONTROLLER_NONE; - s_controller_rumble[i] = 0; - } - int ret = libusb_init(&s_libusb_context); if (ret) @@ -107,129 +102,176 @@ void Init() } else { - libusb_device** list; - ssize_t cnt = libusb_get_device_list(nullptr, &list); - for (int d = 0; d < cnt; d++) + Setup(); + } +} + +void Setup() +{ + int ret; + libusb_device** list; + ssize_t cnt = libusb_get_device_list(s_libusb_context, &list); + + for (int i = 0; i < MAX_SI_CHANNELS; i++) + { + s_controller_type[i] = CONTROLLER_NONE; + s_controller_rumble[i] = 0; + } + + + for (int d = 0; d < cnt; d++) + { + libusb_device* device = list[d]; + libusb_device_descriptor desc; + int dRet = libusb_get_device_descriptor(device, &desc); + if (dRet) { - libusb_device* device = list[d]; - libusb_device_descriptor desc; - int dRet = libusb_get_device_descriptor(device, &desc); - if (dRet) - { - // could not aquire the descriptor, no point in trying to use it. - ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet); - continue; - } + // could not aquire the descriptor, no point in trying to use it. + ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet); + continue; + } - if (desc.idVendor == 0x057e && desc.idProduct == 0x0337) - { - NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d", desc.idVendor, desc.idProduct, 1); + if (desc.idVendor == 0x057e && desc.idProduct == 0x0337) + { + NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d", desc.idVendor, desc.idProduct, 1); - u8 bus = libusb_get_bus_number(device); - u8 port = libusb_get_device_address(device); - ret = libusb_open(device, &s_handle); - if (ret) + u8 bus = libusb_get_bus_number(device); + u8 port = libusb_get_device_address(device); + ret = libusb_open(device, &s_handle); + if (ret) + { + if (ret == LIBUSB_ERROR_ACCESS) { - if (ret == LIBUSB_ERROR_ACCESS) + if (dRet) { - if (dRet) - { - ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID ????:???? (couldn't get id).", - bus, - port - ); - } - else - { - ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.", - bus, - port, - desc.idVendor, - desc.idProduct - ); - } + ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID ????:???? (couldn't get id).", + bus, + port + ); } else { - ERROR_LOG(SERIALINTERFACE, "libusb_open failed to open device with error = %d", ret); - if (ret == LIBUSB_ERROR_NOT_SUPPORTED) - s_libusb_driver_not_supported = true; - } - Shutdown(); - } - else if ((ret = libusb_kernel_driver_active(s_handle, 0)) == 1) - { - if ((ret = libusb_detach_kernel_driver(s_handle, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED) - { - ERROR_LOG(SERIALINTERFACE, "libusb_detach_kernel_driver failed with error: %d", ret); - Shutdown(); + ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.", + bus, + port, + desc.idVendor, + desc.idProduct + ); } } - else if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED) + else { - ERROR_LOG(SERIALINTERFACE, "libusb_kernel_driver_active error ret = %d", ret); - Shutdown(); + ERROR_LOG(SERIALINTERFACE, "libusb_open failed to open device with error = %d", ret); + if (ret == LIBUSB_ERROR_NOT_SUPPORTED) + s_libusb_driver_not_supported = true; } - else if ((ret = libusb_claim_interface(s_handle, 0))) + Shutdown(); + } + else if ((ret = libusb_kernel_driver_active(s_handle, 0)) == 1) + { + if ((ret = libusb_detach_kernel_driver(s_handle, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED) { - ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret); + ERROR_LOG(SERIALINTERFACE, "libusb_detach_kernel_driver failed with error: %d", ret); Shutdown(); } else { - libusb_config_descriptor *config = nullptr; - libusb_get_config_descriptor(device, 0, &config); - for (u8 ic = 0; ic < config->bNumInterfaces; ic++) - { - const libusb_interface *interfaceContainer = &config->interface[ic]; - for (int i = 0; i < interfaceContainer->num_altsetting; i++) - { - const libusb_interface_descriptor *interface = &interfaceContainer->altsetting[i]; - for (int e = 0; e < (int)interface->bNumEndpoints; e++) - { - const libusb_endpoint_descriptor *endpoint = &interface->endpoint[e]; - if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) - s_endpoint_in = endpoint->bEndpointAddress; - else - s_endpoint_out = endpoint->bEndpointAddress; - } - } - } - - int tmp = 0; - unsigned char payload = 0x13; - libusb_interrupt_transfer(s_handle, s_endpoint_out, &payload, sizeof(payload), &tmp, 16); - - if (SConfig::GetInstance().m_GameCubeAdapterThread) - { - s_adapter_thread_running.Set(true); - s_adapter_thread = std::thread(Read); - } - else - { - s_irq_transfer_read = libusb_alloc_transfer(0); - s_irq_transfer_write = libusb_alloc_transfer(0); - libusb_fill_interrupt_transfer(s_irq_transfer_read, s_handle, s_endpoint_in, s_controller_payload_swap, sizeof(s_controller_payload_swap), read_callback, NULL, 16); - libusb_submit_transfer(s_irq_transfer_read); - - s_adapter_thread_running.Set(true); - s_adapter_thread = std::thread(HandleEvents); - } - - s_detected = true; + AddGCAdapter(device); } } + else if ((ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)) + { + ERROR_LOG(SERIALINTERFACE, "libusb_kernel_driver_active error ret = %d", ret); + Shutdown(); + } + else if ((ret = libusb_claim_interface(s_handle, 0))) + { + ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret); + Shutdown(); + } + else + { + AddGCAdapter(device); + } } - - libusb_free_device_list(list, 1); } + + libusb_free_device_list(list, 1); +} + + +void AddGCAdapter(libusb_device* device) +{ + libusb_config_descriptor *config = nullptr; + libusb_get_config_descriptor(device, 0, &config); + for (u8 ic = 0; ic < config->bNumInterfaces; ic++) + { + const libusb_interface *interfaceContainer = &config->interface[ic]; + for (int i = 0; i < interfaceContainer->num_altsetting; i++) + { + const libusb_interface_descriptor *interface = &interfaceContainer->altsetting[i]; + for (u8 e = 0; e < interface->bNumEndpoints; e++) + { + const libusb_endpoint_descriptor *endpoint = &interface->endpoint[e]; + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) + s_endpoint_in = endpoint->bEndpointAddress; + else + s_endpoint_out = endpoint->bEndpointAddress; + } + } + } + + int tmp = 0; + unsigned char payload = 0x13; + libusb_interrupt_transfer(s_handle, s_endpoint_out, &payload, sizeof(payload), &tmp, 16); + + if (SConfig::GetInstance().m_GameCubeAdapterThread) + { + s_adapter_thread_running.Set(true); + s_adapter_thread = std::thread(Read); + } + else + { + s_irq_transfer_read = libusb_alloc_transfer(0); + s_irq_transfer_write = libusb_alloc_transfer(0); + libusb_fill_interrupt_transfer(s_irq_transfer_read, s_handle, s_endpoint_in, s_controller_payload_swap, sizeof(s_controller_payload_swap), read_callback, NULL, 0); + libusb_submit_transfer(s_irq_transfer_read); + + s_adapter_thread_running.Set(true); + s_adapter_thread = std::thread(HandleEvents); + } + + s_detected = true; } void Shutdown() +{ + + Reset(); + + if (s_libusb_context) + { + libusb_exit(s_libusb_context); + s_libusb_context = nullptr; + } + + s_libusb_driver_not_supported = false; +} + +void Reset() { if (!SConfig::GetInstance().m_GameCubeAdapter) return; + if (!SConfig::GetInstance().m_GameCubeAdapterThread) + { + + if (s_irq_transfer_read) + libusb_cancel_transfer(s_irq_transfer_read); + if (s_irq_transfer_write) + libusb_cancel_transfer(s_irq_transfer_write); + } + if (s_adapter_thread_running.TestAndClear()) { s_adapter_thread.join(); @@ -251,16 +293,9 @@ void Shutdown() for (int i = 0; i < MAX_SI_CHANNELS; i++) s_controller_type[i] = CONTROLLER_NONE; - - if (s_libusb_context) - { - libusb_exit(s_libusb_context); - s_libusb_context = nullptr; - } - - s_libusb_driver_not_supported = false; } + void Input(int chan, GCPadStatus* pad) { if (!SConfig::GetInstance().m_GameCubeAdapter) diff --git a/Source/Core/Core/HW/SI_GCAdapter.h b/Source/Core/Core/HW/SI_GCAdapter.h index 4e11dd6636..925e044b94 100644 --- a/Source/Core/Core/HW/SI_GCAdapter.h +++ b/Source/Core/Core/HW/SI_GCAdapter.h @@ -4,6 +4,8 @@ #pragma once +struct libusb_device; + #include "Common/Thread.h" #include "Core/HW/SI.h" #include "InputCommon/GCPadStatus.h" @@ -12,7 +14,10 @@ namespace SI_GCAdapter { void Init(); +void Reset(); +void Setup(); void Shutdown(); +void AddGCAdapter(libusb_device* device); void Input(int chan, GCPadStatus* pad); void Output(int chan, u8 rumble_command); bool IsDetected();