Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a background thread to detect the GC adapter #2330

Merged
merged 2 commits into from
May 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Source/Core/Core/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/ConfigManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ struct SConfig : NonCopyable
// Input settings
bool m_BackgroundInput;
bool m_GameCubeAdapter;
bool m_AdapterRumble;

SysConf* m_SYSCONF;

Expand Down
244 changes: 161 additions & 83 deletions Source/Core/Core/HW/SI_GCAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -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;

This comment was marked as off-topic.


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;
Expand All @@ -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)
Expand All @@ -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);

Expand All @@ -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 (CheckDeviceAccess(device))
AddGCAdapter(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);
libusb_free_device_list(list, 1);
}

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;
}

u8 bus = libusb_get_bus_number(device);
u8 port = libusb_get_device_address(device);
ret = libusb_open(device, &s_handle);
if (ret)
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 (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;
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 = 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)))
else
{
ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret);
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
}
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)
{
AddGCAdapter(device);
break;
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;
}
}

libusb_free_device_list(list, 1);

if (!s_detected)
Shutdown();
return false;
}


void AddGCAdapter(libusb_device* device)
static void AddGCAdapter(libusb_device* device)
{
libusb_config_descriptor *config = nullptr;
libusb_get_config_descriptor(device, 0, &config);
Expand Down Expand Up @@ -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)
Expand All @@ -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];

Expand All @@ -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
{
Expand Down Expand Up @@ -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
Expand All @@ -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();
}
}
}
Expand Down