mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-31 10:09:36 -06:00
Merge pull request #2330 from mathieui/background-gc-adapter-scan
Add a background thread to detect the GC adapter
This commit is contained in:
@ -354,6 +354,7 @@ void SConfig::SaveCoreSettings(IniFile& ini)
|
||||
core->Set("GFXBackend", m_LocalCoreStartupParameter.m_strVideoBackend);
|
||||
core->Set("GPUDeterminismMode", m_LocalCoreStartupParameter.m_strGPUDeterminismMode);
|
||||
core->Set("GameCubeAdapter", m_GameCubeAdapter);
|
||||
core->Set("AdapterRumble", m_AdapterRumble);
|
||||
}
|
||||
|
||||
void SConfig::SaveMovieSettings(IniFile& ini)
|
||||
@ -621,6 +622,7 @@ void SConfig::LoadCoreSettings(IniFile& ini)
|
||||
core->Get("GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, "");
|
||||
core->Get("GPUDeterminismMode", &m_LocalCoreStartupParameter.m_strGPUDeterminismMode, "auto");
|
||||
core->Get("GameCubeAdapter", &m_GameCubeAdapter, true);
|
||||
core->Get("AdapterRumble", &m_AdapterRumble, true);
|
||||
}
|
||||
|
||||
void SConfig::LoadMovieSettings(IniFile& ini)
|
||||
|
@ -116,6 +116,7 @@ struct SConfig : NonCopyable
|
||||
// Input settings
|
||||
bool m_BackgroundInput;
|
||||
bool m_GameCubeAdapter;
|
||||
bool m_AdapterRumble;
|
||||
|
||||
SysConf* m_SYSCONF;
|
||||
|
||||
|
@ -19,6 +19,9 @@ enum ControllerTypes
|
||||
CONTROLLER_WIRELESS = 2
|
||||
};
|
||||
|
||||
static bool CheckDeviceAccess(libusb_device* device);
|
||||
static void AddGCAdapter(libusb_device* device);
|
||||
|
||||
static bool s_detected = false;
|
||||
static libusb_device_handle* s_handle = nullptr;
|
||||
static u8 s_controller_type[MAX_SI_CHANNELS] = { CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE };
|
||||
@ -33,8 +36,15 @@ static int s_controller_payload_size = 0;
|
||||
static std::thread s_adapter_thread;
|
||||
static Common::Flag s_adapter_thread_running;
|
||||
|
||||
static std::thread s_adapter_detect_thread;
|
||||
static Common::Flag s_adapter_detect_thread_running;
|
||||
|
||||
static std::function<void(void)> s_detect_callback;
|
||||
|
||||
static bool s_libusb_driver_not_supported = false;
|
||||
static libusb_context* s_libusb_context = nullptr;
|
||||
static bool s_libusb_hotplug_enabled = false;
|
||||
static libusb_hotplug_callback_handle s_hotplug_handle;
|
||||
|
||||
static u8 s_endpoint_in = 0;
|
||||
static u8 s_endpoint_out = 0;
|
||||
@ -56,6 +66,61 @@ static void Read()
|
||||
}
|
||||
}
|
||||
|
||||
static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotplug_event event, void* user_data)
|
||||
{
|
||||
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
|
||||
{
|
||||
if (s_handle == nullptr && CheckDeviceAccess(dev))
|
||||
AddGCAdapter(dev);
|
||||
}
|
||||
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
{
|
||||
if (s_handle != nullptr && libusb_get_device(s_handle) == dev)
|
||||
Reset();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ScanThreadFunc()
|
||||
{
|
||||
Common::SetCurrentThreadName("GC Adapter Scanning Thread");
|
||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread started");
|
||||
|
||||
s_libusb_hotplug_enabled = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) != 0;
|
||||
if (s_libusb_hotplug_enabled)
|
||||
{
|
||||
if (libusb_hotplug_register_callback(s_libusb_context, (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), LIBUSB_HOTPLUG_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback, NULL, &s_hotplug_handle) != LIBUSB_SUCCESS)
|
||||
s_libusb_hotplug_enabled = false;
|
||||
if (s_libusb_hotplug_enabled)
|
||||
NOTICE_LOG(SERIALINTERFACE, "Using libUSB hotplug detection");
|
||||
}
|
||||
|
||||
while (s_adapter_detect_thread_running.IsSet())
|
||||
{
|
||||
if (s_libusb_hotplug_enabled)
|
||||
{
|
||||
static timeval tv = {0, 500000};
|
||||
libusb_handle_events_timeout(s_libusb_context, &tv);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_handle == nullptr)
|
||||
{
|
||||
Setup();
|
||||
if (s_detected && s_detect_callback != nullptr)
|
||||
s_detect_callback();
|
||||
}
|
||||
Common::SleepCurrentThread(500);
|
||||
}
|
||||
}
|
||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped");
|
||||
}
|
||||
|
||||
void SetAdapterCallback(std::function<void(void)> func)
|
||||
{
|
||||
s_detect_callback = func;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
if (s_handle != nullptr)
|
||||
@ -80,13 +145,26 @@ void Init()
|
||||
}
|
||||
else
|
||||
{
|
||||
Setup();
|
||||
StartScanThread();
|
||||
}
|
||||
}
|
||||
|
||||
void StartScanThread()
|
||||
{
|
||||
s_adapter_detect_thread_running.Set(true);
|
||||
s_adapter_detect_thread = std::thread(ScanThreadFunc);
|
||||
}
|
||||
|
||||
void StopScanThread()
|
||||
{
|
||||
if (s_adapter_detect_thread_running.TestAndClear())
|
||||
{
|
||||
s_adapter_detect_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Setup()
|
||||
{
|
||||
int ret;
|
||||
libusb_device** list;
|
||||
ssize_t cnt = libusb_get_device_list(s_libusb_context, &list);
|
||||
|
||||
@ -96,87 +174,89 @@ void Setup()
|
||||
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)
|
||||
{
|
||||
// could not acquire 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);
|
||||
|
||||
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 (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
|
||||
);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
// this split is needed so that we don't avoid claiming the interface when
|
||||
// detaching the kernel driver is successful
|
||||
if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if ((ret = libusb_claim_interface(s_handle, 0)))
|
||||
{
|
||||
ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddGCAdapter(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (CheckDeviceAccess(device))
|
||||
AddGCAdapter(device);
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
|
||||
if (!s_detected)
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
static bool CheckDeviceAccess(libusb_device* device)
|
||||
{
|
||||
int ret;
|
||||
libusb_device_descriptor desc;
|
||||
int dRet = libusb_get_device_descriptor(device, &desc);
|
||||
if (dRet)
|
||||
{
|
||||
// could not acquire the descriptor, no point in trying to use it.
|
||||
ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet);
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddGCAdapter(libusb_device* device)
|
||||
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)
|
||||
{
|
||||
if (ret == LIBUSB_ERROR_ACCESS)
|
||||
{
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
// this split is needed so that we don't avoid claiming the interface when
|
||||
// detaching the kernel driver is successful
|
||||
if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((ret = libusb_claim_interface(s_handle, 0)))
|
||||
{
|
||||
ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void AddGCAdapter(libusb_device* device)
|
||||
{
|
||||
libusb_config_descriptor *config = nullptr;
|
||||
libusb_get_config_descriptor(device, 0, &config);
|
||||
@ -205,10 +285,15 @@ void AddGCAdapter(libusb_device* device)
|
||||
s_adapter_thread = std::thread(Read);
|
||||
|
||||
s_detected = true;
|
||||
if (s_detect_callback != nullptr)
|
||||
s_detect_callback();
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
StopScanThread();
|
||||
if (s_libusb_hotplug_enabled)
|
||||
libusb_hotplug_deregister_callback(s_libusb_context, s_hotplug_handle);
|
||||
Reset();
|
||||
|
||||
if (s_libusb_context)
|
||||
@ -230,36 +315,29 @@ void Reset()
|
||||
s_adapter_thread.join();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
||||
s_controller_type[i] = CONTROLLER_NONE;
|
||||
|
||||
s_detected = false;
|
||||
|
||||
if (s_handle)
|
||||
{
|
||||
libusb_release_interface(s_handle, 0);
|
||||
libusb_close(s_handle);
|
||||
s_handle = nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
||||
s_controller_type[i] = CONTROLLER_NONE;
|
||||
if (s_detect_callback != nullptr)
|
||||
s_detect_callback();
|
||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter detached");
|
||||
}
|
||||
|
||||
|
||||
void Input(int chan, GCPadStatus* pad)
|
||||
{
|
||||
if (!SConfig::GetInstance().m_GameCubeAdapter)
|
||||
return;
|
||||
|
||||
if (s_handle == nullptr)
|
||||
{
|
||||
if (s_detected)
|
||||
{
|
||||
Init();
|
||||
if (s_handle == nullptr)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (s_handle == nullptr || !s_detected)
|
||||
return;
|
||||
|
||||
u8 controller_payload_copy[37];
|
||||
|
||||
@ -271,7 +349,7 @@ void Input(int chan, GCPadStatus* pad)
|
||||
if (s_controller_payload_size != sizeof(controller_payload_copy) || controller_payload_copy[0] != LIBUSB_DT_HID)
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", s_controller_payload_size, controller_payload_copy[0]);
|
||||
Shutdown();
|
||||
Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -314,7 +392,7 @@ void Input(int chan, GCPadStatus* pad)
|
||||
|
||||
void Output(int chan, u8 rumble_command)
|
||||
{
|
||||
if (s_handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter)
|
||||
if (s_handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter || !SConfig::GetInstance().m_AdapterRumble)
|
||||
return;
|
||||
|
||||
// Skip over rumble commands if it has not changed or the controller is wireless
|
||||
@ -330,7 +408,7 @@ void Output(int chan, u8 rumble_command)
|
||||
if (size != 0x05 && size != 0x00)
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size);
|
||||
Shutdown();
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
struct libusb_device;
|
||||
#include <functional>
|
||||
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/HW/SI.h"
|
||||
@ -17,7 +17,9 @@ void Init();
|
||||
void Reset();
|
||||
void Setup();
|
||||
void Shutdown();
|
||||
void AddGCAdapter(libusb_device* device);
|
||||
void SetAdapterCallback(std::function<void(void)> func);
|
||||
void StartScanThread();
|
||||
void StopScanThread();
|
||||
void Input(int chan, GCPadStatus* pad);
|
||||
void Output(int chan, u8 rumble_command);
|
||||
bool IsDetected();
|
||||
|
Reference in New Issue
Block a user