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.
This commit is contained in:
mathieui 2015-01-26 13:54:54 +01:00
parent a2b872b9da
commit 8e556603af
2 changed files with 150 additions and 110 deletions

View File

@ -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,8 +102,23 @@ void Init()
}
else
{
Setup();
}
}
void Setup()
{
int ret;
libusb_device** list;
ssize_t cnt = libusb_get_device_list(nullptr, &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];
@ -164,8 +174,12 @@ void Init()
ERROR_LOG(SERIALINTERFACE, "libusb_detach_kernel_driver failed with error: %d", ret);
Shutdown();
}
else
{
AddGCAdapter(device);
}
else if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
}
else if ((ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED))
{
ERROR_LOG(SERIALINTERFACE, "libusb_kernel_driver_active error ret = %d", ret);
Shutdown();
@ -176,6 +190,17 @@ void Init()
Shutdown();
}
else
{
AddGCAdapter(device);
}
}
}
libusb_free_device_list(list, 1);
}
void AddGCAdapter(libusb_device* device)
{
libusb_config_descriptor *config = nullptr;
libusb_get_config_descriptor(device, 0, &config);
@ -185,7 +210,7 @@ void Init()
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++)
for (u8 e = 0; e < interface->bNumEndpoints; e++)
{
const libusb_endpoint_descriptor *endpoint = &interface->endpoint[e];
if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN)
@ -209,7 +234,7 @@ void Init()
{
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_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);
@ -218,18 +243,35 @@ void Init()
s_detected = true;
}
}
}
libusb_free_device_list(list, 1);
}
}
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,15 +293,8 @@ 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)
{

View File

@ -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();