From c8a6dc6c23a5efe99b4bf937003ba3f2f7f8f200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 15 Jan 2017 20:50:26 +0100 Subject: [PATCH] Use a single libusb context libusb on Windows is limited to only a single context. Trying to open more than one can cause device enumerations to fail randomly. libusb is thread-safe and we don't use the manual polling support (with `poll()`) so this should be safe. --- Source/Core/Common/CMakeLists.txt | 5 ++ Source/Core/Common/Common.vcxproj | 4 ++ Source/Core/Common/Common.vcxproj.filters | 2 + Source/Core/Common/LibusbContext.cpp | 46 +++++++++++++++++++ Source/Core/Common/LibusbContext.h | 15 ++++++ Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp | 11 ++--- Source/Core/Core/IOS/USB/Bluetooth/BTReal.h | 2 +- Source/Core/Core/IOS/USB/Host.cpp | 17 ++----- Source/Core/Core/IOS/USB/Host.h | 4 +- Source/Core/InputCommon/GCAdapter.cpp | 27 +++++------ Source/Core/UICommon/USBUtils.cpp | 11 +++-- 11 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 Source/Core/Common/LibusbContext.cpp create mode 100644 Source/Core/Common/LibusbContext.h diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 2a5ff1d4d8..5f910a1670 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -34,6 +34,11 @@ set(SRCS Analytics.cpp Crypto/ec.cpp Logging/LogManager.cpp) +if(LIBUSB_FOUND) + set(LIBS ${LIBS} ${LIBUSB_LIBRARIES}) + set(SRCS ${SRCS} LibusbContext.cpp) +endif(LIBUSB_FOUND) + if(ANDROID) set(SRCS ${SRCS} Logging/ConsoleListenerDroid.cpp) diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 52f408e776..831dcad4a7 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -113,6 +113,7 @@ + @@ -159,6 +160,9 @@ + + 4200;%(DisableSpecificWarnings) + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 7731482020..3c9b952742 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -45,6 +45,7 @@ + @@ -236,6 +237,7 @@ + diff --git a/Source/Core/Common/LibusbContext.cpp b/Source/Core/Common/LibusbContext.cpp new file mode 100644 index 0000000000..a2ed128200 --- /dev/null +++ b/Source/Core/Common/LibusbContext.cpp @@ -0,0 +1,46 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/LibusbContext.h" +#include "Common/MsgHandler.h" + +namespace LibusbContext +{ +static std::shared_ptr s_libusb_context; +static std::once_flag s_context_initialized; + +static libusb_context* Create() +{ + libusb_context* context; + const int ret = libusb_init(&context); + if (ret < LIBUSB_SUCCESS) + { + bool is_windows = false; +#ifdef _WIN32 + is_windows = true; +#endif + if (is_windows && ret == LIBUSB_ERROR_NOT_FOUND) + PanicAlertT("Failed to initialize libusb because usbdk is not installed."); + else + PanicAlertT("Failed to initialize libusb: %s", libusb_error_name(ret)); + return nullptr; + } + return context; +} + +std::shared_ptr Get() +{ + std::call_once(s_context_initialized, []() { + s_libusb_context.reset(Create(), [](auto* context) { + if (context != nullptr) + libusb_exit(context); + }); + }); + return s_libusb_context; +} +} diff --git a/Source/Core/Common/LibusbContext.h b/Source/Core/Common/LibusbContext.h new file mode 100644 index 0000000000..99af12ba4c --- /dev/null +++ b/Source/Core/Common/LibusbContext.h @@ -0,0 +1,15 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +struct libusb_context; + +namespace LibusbContext +{ +// libusb on Windows is limited to only a single context. Trying to open more +// than one can cause issues with device enumerations. +// libusb is thread-safe so this context can be safely used from different threads. +std::shared_ptr Get(); +} diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp index 65390e5d49..0e7267b614 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp @@ -16,6 +16,7 @@ #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonFuncs.h" +#include "Common/LibusbContext.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/Network.h" @@ -59,8 +60,8 @@ namespace Device BluetoothReal::BluetoothReal(u32 device_id, const std::string& device_name) : BluetoothBase(device_id, device_name) { - const int ret = libusb_init(&m_libusb_context); - _assert_msg_(IOS_WIIMOTE, ret == 0, "Failed to init libusb."); + m_libusb_context = LibusbContext::Get(); + _assert_msg_(IOS_WIIMOTE, m_libusb_context, "Failed to init libusb."); LoadLinkKeys(); } @@ -78,15 +79,13 @@ BluetoothReal::~BluetoothReal() libusb_unref_device(m_device); } - libusb_exit(m_libusb_context); - SaveLinkKeys(); } ReturnCode BluetoothReal::Open(const OpenRequest& request) { libusb_device** list; - const ssize_t cnt = libusb_get_device_list(m_libusb_context, &list); + const ssize_t cnt = libusb_get_device_list(m_libusb_context.get(), &list); _dbg_assert_msg_(IOS, cnt > 0, "Couldn't get device list"); for (ssize_t i = 0; i < cnt; ++i) { @@ -601,7 +600,7 @@ void BluetoothReal::TransferThread() Common::SetCurrentThreadName("BT USB Thread"); while (m_thread_running.IsSet()) { - libusb_handle_events_completed(m_libusb_context, nullptr); + libusb_handle_events_completed(m_libusb_context.get(), nullptr); } } diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h index dc1679925a..ba98483c7c 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h @@ -76,7 +76,7 @@ private: libusb_device* m_device = nullptr; libusb_device_handle* m_handle = nullptr; - libusb_context* m_libusb_context = nullptr; + std::shared_ptr m_libusb_context; Common::Flag m_thread_running; std::thread m_thread; diff --git a/Source/Core/Core/IOS/USB/Host.cpp b/Source/Core/Core/IOS/USB/Host.cpp index 14fd0ebe29..875dc451f1 100644 --- a/Source/Core/Core/IOS/USB/Host.cpp +++ b/Source/Core/Core/IOS/USB/Host.cpp @@ -13,6 +13,7 @@ #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +#include "Common/LibusbContext.h" #include "Common/Logging/Log.h" #include "Common/Thread.h" #include "Core/ConfigManager.h" @@ -30,16 +31,8 @@ namespace Device USBHost::USBHost(u32 device_id, const std::string& device_name) : Device(device_id, device_name) { #ifdef __LIBUSB__ - const int ret = libusb_init(&m_libusb_context); - _assert_msg_(WII_IPC_USB, ret == 0, "Failed to init libusb."); - libusb_set_debug(m_libusb_context, LIBUSB_LOG_LEVEL_WARNING); -#endif -} - -USBHost::~USBHost() -{ -#ifdef __LIBUSB__ - libusb_exit(m_libusb_context); + m_libusb_context = LibusbContext::Get(); + _assert_msg_(IOS_USB, m_libusb_context, "Failed to init libusb."); #endif } @@ -125,7 +118,7 @@ bool USBHost::AddNewDevices(std::set& new_devices, DeviceChangeHooks& hooks { #ifdef __LIBUSB__ libusb_device** list; - const ssize_t count = libusb_get_device_list(m_libusb_context, &list); + const ssize_t count = libusb_get_device_list(m_libusb_context.get(), &list); if (count < 0) { WARN_LOG(IOS_USB, "Failed to get device list: %s", libusb_error_name(static_cast(count))); @@ -210,7 +203,7 @@ void USBHost::StartThreads() while (m_event_thread_running.IsSet()) { static timeval tv = {0, 50000}; - libusb_handle_events_timeout_completed(m_libusb_context, &tv, nullptr); + libusb_handle_events_timeout_completed(m_libusb_context.get(), &tv, nullptr); } }); } diff --git a/Source/Core/Core/IOS/USB/Host.h b/Source/Core/Core/IOS/USB/Host.h index 15d8c4212d..40c09ca709 100644 --- a/Source/Core/Core/IOS/USB/Host.h +++ b/Source/Core/Core/IOS/USB/Host.h @@ -34,7 +34,7 @@ class USBHost : public Device { public: USBHost(u32 device_id, const std::string& device_name); - virtual ~USBHost(); + virtual ~USBHost() = default; ReturnCode Open(const OpenRequest& request) override; @@ -71,7 +71,7 @@ private: void DispatchHooks(const DeviceChangeHooks& hooks); #ifdef __LIBUSB__ - libusb_context* m_libusb_context = nullptr; + std::shared_ptr m_libusb_context; // Event thread for libusb Common::Flag m_event_thread_running; std::thread m_event_thread; diff --git a/Source/Core/InputCommon/GCAdapter.cpp b/Source/Core/InputCommon/GCAdapter.cpp index e3dcb44739..fae7a81055 100644 --- a/Source/Core/InputCommon/GCAdapter.cpp +++ b/Source/Core/InputCommon/GCAdapter.cpp @@ -4,9 +4,11 @@ #include #include +#include #include #include "Common/Flag.h" +#include "Common/LibusbContext.h" #include "Common/Logging/Log.h" #include "Common/Thread.h" #include "Core/ConfigManager.h" @@ -50,7 +52,7 @@ static Common::Flag s_adapter_detect_thread_running; static std::function s_detect_callback; static bool s_libusb_driver_not_supported = false; -static libusb_context* s_libusb_context = nullptr; +static std::shared_ptr s_libusb_context; #if defined(__FreeBSD__) && __FreeBSD__ >= 11 static bool s_libusb_hotplug_enabled = true; #else @@ -116,8 +118,8 @@ static void ScanThreadFunc() 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), + s_libusb_context.get(), (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), LIBUSB_HOTPLUG_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback, nullptr, &s_hotplug_handle) != LIBUSB_SUCCESS) s_libusb_hotplug_enabled = false; @@ -131,7 +133,7 @@ static void ScanThreadFunc() if (s_libusb_hotplug_enabled) { static timeval tv = {0, 500000}; - libusb_handle_events_timeout(s_libusb_context, &tv); + libusb_handle_events_timeout(s_libusb_context.get(), &tv); } else { @@ -168,11 +170,9 @@ void Init() s_libusb_driver_not_supported = false; - int ret = libusb_init(&s_libusb_context); - - if (ret) + s_libusb_context = LibusbContext::Get(); + if (!s_libusb_context) { - ERROR_LOG(SERIALINTERFACE, "libusb_init failed with error: %d", ret); s_libusb_driver_not_supported = true; Shutdown(); } @@ -203,7 +203,7 @@ void StopScanThread() static void Setup() { libusb_device** list; - ssize_t cnt = libusb_get_device_list(s_libusb_context, &list); + ssize_t cnt = libusb_get_device_list(s_libusb_context.get(), &list); for (int i = 0; i < MAX_SI_CHANNELS; i++) { @@ -335,16 +335,11 @@ void Shutdown() StopScanThread(); #if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102 if (s_libusb_hotplug_enabled) - libusb_hotplug_deregister_callback(s_libusb_context, s_hotplug_handle); + libusb_hotplug_deregister_callback(s_libusb_context.get(), s_hotplug_handle); #endif Reset(); - if (s_libusb_context) - { - libusb_exit(s_libusb_context); - s_libusb_context = nullptr; - } - + s_libusb_context.reset(); s_libusb_driver_not_supported = false; } diff --git a/Source/Core/UICommon/USBUtils.cpp b/Source/Core/UICommon/USBUtils.cpp index 8f0ba21d52..da7c603580 100644 --- a/Source/Core/UICommon/USBUtils.cpp +++ b/Source/Core/UICommon/USBUtils.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include + #ifdef __LIBUSB__ #include +#include "Common/LibusbContext.h" #endif #include "Common/CommonTypes.h" @@ -11,7 +14,7 @@ #include "UICommon/USBUtils.h" #ifdef __LIBUSB__ -static libusb_context* s_libusb_context; +static std::shared_ptr s_libusb_context; #endif // Because opening and getting the device name from devices is slow, especially on Windows @@ -36,14 +39,14 @@ namespace USBUtils void Init() { #ifdef __LIBUSB__ - libusb_init(&s_libusb_context); + s_libusb_context = LibusbContext::Get(); #endif } void Shutdown() { #ifdef __LIBUSB__ - libusb_exit(s_libusb_context); + s_libusb_context = nullptr; #endif } @@ -56,7 +59,7 @@ std::map, std::string> GetInsertedDevices() return devices; libusb_device** list; - const ssize_t cnt = libusb_get_device_list(s_libusb_context, &list); + const ssize_t cnt = libusb_get_device_list(s_libusb_context.get(), &list); for (ssize_t i = 0; i < cnt; ++i) { libusb_device_descriptor descr;