mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-19 04:21:19 +01:00
cf60a9a7f7
Unfortunately, it appears that using libusb's synchronous transfer API from several threads causes nasty race conditions in event handling and can lead to deadlocks, despite the fact that libusb's synchronous API is documented to be perfectly fine to use from several threads (only the manual polling functionality is supposed to require special precautions). Since usbdk was the only real reason for using a single libusb context and since usbdk (currently) has so many issues with Dolphin, I think dropping support for it in order to fix other backends is acceptable.
122 lines
2.7 KiB
C++
122 lines
2.7 KiB
C++
// Copyright 2019 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
#if defined(__LIBUSB__)
|
|
#include <libusb.h>
|
|
#endif
|
|
|
|
#include "Common/Assert.h"
|
|
#include "Common/Flag.h"
|
|
#include "Common/Thread.h"
|
|
#include "Core/LibusbUtils.h"
|
|
|
|
namespace LibusbUtils
|
|
{
|
|
#if defined(__LIBUSB__)
|
|
class Context::Impl
|
|
{
|
|
public:
|
|
Impl()
|
|
{
|
|
const int ret = libusb_init(&m_context);
|
|
ASSERT_MSG(IOS_USB, ret == LIBUSB_SUCCESS, "Failed to init libusb: %s", libusb_error_name(ret));
|
|
if (ret != LIBUSB_SUCCESS)
|
|
return;
|
|
|
|
#ifdef _WIN32
|
|
libusb_set_option(m_context, LIBUSB_OPTION_USE_USBDK);
|
|
#endif
|
|
m_event_thread_running.Set();
|
|
m_event_thread = std::thread(&Impl::EventThread, this);
|
|
}
|
|
|
|
~Impl()
|
|
{
|
|
if (!m_context || !m_event_thread_running.TestAndClear())
|
|
return;
|
|
#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000105
|
|
libusb_interrupt_event_handler(m_context);
|
|
#endif
|
|
m_event_thread.join();
|
|
libusb_exit(m_context);
|
|
}
|
|
|
|
libusb_context* GetContext() { return m_context; }
|
|
|
|
bool GetDeviceList(GetDeviceListCallback callback)
|
|
{
|
|
std::lock_guard lock{m_device_list_mutex};
|
|
|
|
libusb_device** list;
|
|
ssize_t count = libusb_get_device_list(m_context, &list);
|
|
if (count < 0)
|
|
return false;
|
|
|
|
for (ssize_t i = 0; i < count; ++i)
|
|
{
|
|
if (!callback(list[i]))
|
|
break;
|
|
}
|
|
libusb_free_device_list(list, 1);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
void EventThread()
|
|
{
|
|
Common::SetCurrentThreadName("libusb thread");
|
|
timeval tv{5, 0};
|
|
while (m_event_thread_running.IsSet())
|
|
libusb_handle_events_timeout_completed(m_context, &tv, nullptr);
|
|
}
|
|
|
|
libusb_context* m_context = nullptr;
|
|
std::mutex m_device_list_mutex;
|
|
Common::Flag m_event_thread_running;
|
|
std::thread m_event_thread;
|
|
};
|
|
#else
|
|
class Context::Impl
|
|
{
|
|
public:
|
|
libusb_context* GetContext() { return nullptr; }
|
|
bool GetDeviceList(GetDeviceListCallback callback) { return false; }
|
|
};
|
|
#endif
|
|
|
|
Context::Context() : m_impl{std::make_unique<Impl>()}
|
|
{
|
|
}
|
|
|
|
Context::~Context() = default;
|
|
|
|
Context::operator libusb_context*() const
|
|
{
|
|
return m_impl->GetContext();
|
|
}
|
|
|
|
bool Context::IsValid() const
|
|
{
|
|
return m_impl->GetContext() != nullptr;
|
|
}
|
|
|
|
bool Context::GetDeviceList(GetDeviceListCallback callback)
|
|
{
|
|
return m_impl->GetDeviceList(std::move(callback));
|
|
}
|
|
|
|
ConfigDescriptor MakeConfigDescriptor(libusb_device* device, u8 config_num)
|
|
{
|
|
#if defined(__LIBUSB__)
|
|
libusb_config_descriptor* descriptor = nullptr;
|
|
if (libusb_get_config_descriptor(device, config_num, &descriptor) == LIBUSB_SUCCESS)
|
|
return {descriptor, libusb_free_config_descriptor};
|
|
#endif
|
|
return {nullptr, [](auto) {}};
|
|
}
|
|
} // namespace LibusbUtils
|