GCAdapter: Report libusb open errors to the user

If opening the adapter fails, report the libusb error message in the GUI
instead of “No Adapter Detected”.

The error condition is removed when the adapter is unplugged.
This commit is contained in:
Vincent Duvert 2018-07-17 22:31:18 +02:00
parent 0165e5e703
commit b08e2ec959
4 changed files with 60 additions and 24 deletions

View File

@ -391,9 +391,9 @@ void DolphinAnalytics::MakePerGameBuilder()
// We grab enough to tell what percentage of our users are playing with keyboard/mouse, some kind // We grab enough to tell what percentage of our users are playing with keyboard/mouse, some kind
// of gamepad // of gamepad
// or the official gamecube adapter. // or the official gamecube adapter.
builder.AddData("gcadapter-detected", GCAdapter::IsDetected()); builder.AddData("gcadapter-detected", GCAdapter::IsDetected(nullptr));
builder.AddData("has-controller", Pad::GetConfig()->IsControllerControlledByGamepadDevice(0) || builder.AddData("has-controller", Pad::GetConfig()->IsControllerControlledByGamepadDevice(0) ||
GCAdapter::IsDetected()); GCAdapter::IsDetected(nullptr));
m_per_game_builder = builder; m_per_game_builder = builder;
} }

View File

@ -28,9 +28,25 @@ void GCPadWiiUConfigDialog::CreateLayout()
{ {
setWindowTitle(tr("GameCube Adapter for Wii U at Port %1").arg(m_port + 1)); setWindowTitle(tr("GameCube Adapter for Wii U at Port %1").arg(m_port + 1));
const bool detected = GCAdapter::IsDetected(); const char* error_message = nullptr;
const bool detected = GCAdapter::IsDetected(&error_message);
QString status_text;
if (detected)
{
status_text = tr("Adapter Detected");
}
else if (error_message)
{
status_text = tr("Error Opening Adapter: %1").arg(QString::fromUtf8(error_message));
}
else
{
status_text = tr("No Adapter Detected");
}
m_layout = new QVBoxLayout(); m_layout = new QVBoxLayout();
m_status_label = new QLabel(detected ? tr("Adapter Detected") : tr("No Adapter Detected")); m_status_label = new QLabel(status_text);
m_rumble = new QCheckBox(tr("Enable Rumble")); m_rumble = new QCheckBox(tr("Enable Rumble"));
m_simulate_bongos = new QCheckBox(tr("Simulate DK Bongos")); m_simulate_bongos = new QCheckBox(tr("Simulate DK Bongos"));
m_button_box = new QDialogButtonBox(QDialogButtonBox::Ok); m_button_box = new QDialogButtonBox(QDialogButtonBox::Ok);

View File

@ -9,6 +9,7 @@
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/Flag.h" #include "Common/Flag.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
@ -29,7 +30,14 @@ static void ResetRumbleLockNeeded();
static void Reset(); static void Reset();
static void Setup(); static void Setup();
static bool s_detected = false; enum
{
NO_ADAPTER_DETECTED = 0,
ADAPTER_DETECTED = 1,
};
// Current adapter status: detected/not detected/in error (holds the error code)
static int s_status = NO_ADAPTER_DETECTED;
static libusb_device_handle* s_handle = nullptr; static libusb_device_handle* s_handle = nullptr;
static u8 s_controller_type[SerialInterface::MAX_SI_CHANNELS] = { static u8 s_controller_type[SerialInterface::MAX_SI_CHANNELS] = {
ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE,
@ -54,7 +62,6 @@ static Common::Flag s_adapter_detect_thread_running;
static std::function<void(void)> s_detect_callback; static std::function<void(void)> s_detect_callback;
static bool s_libusb_driver_not_supported = false;
#if defined(__FreeBSD__) && __FreeBSD__ >= 11 #if defined(__FreeBSD__) && __FreeBSD__ >= 11
static bool s_libusb_hotplug_enabled = true; static bool s_libusb_hotplug_enabled = true;
#else #else
@ -120,6 +127,10 @@ static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotpl
{ {
if (s_handle != nullptr && libusb_get_device(s_handle) == dev) if (s_handle != nullptr && libusb_get_device(s_handle) == dev)
Reset(); Reset();
// Reset a potential error status now that the adapter is unplugged
if (s_status < 0)
s_status = 0;
} }
return 0; return 0;
} }
@ -158,7 +169,7 @@ static void ScanThreadFunc()
{ {
std::lock_guard<std::mutex> lk(s_init_mutex); std::lock_guard<std::mutex> lk(s_init_mutex);
Setup(); Setup();
if (s_detected && s_detect_callback != nullptr) if (s_status == ADAPTER_DETECTED && s_detect_callback != nullptr)
s_detect_callback(); s_detect_callback();
} }
Common::SleepCurrentThread(500); Common::SleepCurrentThread(500);
@ -184,7 +195,7 @@ void Init()
s_last_init = CoreTiming::GetTicks(); s_last_init = CoreTiming::GetTicks();
} }
s_libusb_driver_not_supported = false; s_status = NO_ADAPTER_DETECTED;
if (UseAdapter()) if (UseAdapter())
StartScanThread(); StartScanThread();
@ -247,6 +258,9 @@ static bool CheckDeviceAccess(libusb_device* device)
NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d", NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d",
desc.idVendor, desc.idProduct, 1); desc.idVendor, desc.idProduct, 1);
// In case of failure, capture the libusb error code into the adapter status
Common::ScopeGuard status_guard([&ret] { s_status = ret; });
u8 bus = libusb_get_bus_number(device); u8 bus = libusb_get_bus_number(device);
u8 port = libusb_get_device_address(device); u8 port = libusb_get_device_address(device);
ret = libusb_open(device, &s_handle); ret = libusb_open(device, &s_handle);
@ -260,8 +274,6 @@ static bool CheckDeviceAccess(libusb_device* device)
if (ret) if (ret)
{ {
ERROR_LOG(SERIALINTERFACE, "libusb_open failed to open device 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;
return false; return false;
} }
@ -291,6 +303,9 @@ static bool CheckDeviceAccess(libusb_device* device)
return false; return false;
} }
// Updating the adapter status will be done in AddGCAdapter
status_guard.Dismiss();
return true; return true;
} }
@ -323,7 +338,7 @@ static void AddGCAdapter(libusb_device* device)
s_adapter_input_thread = std::thread(Read); s_adapter_input_thread = std::thread(Read);
s_adapter_output_thread = std::thread(Write); s_adapter_output_thread = std::thread(Write);
s_detected = true; s_status = ADAPTER_DETECTED;
if (s_detect_callback != nullptr) if (s_detect_callback != nullptr)
s_detect_callback(); s_detect_callback();
ResetRumbleLockNeeded(); ResetRumbleLockNeeded();
@ -338,7 +353,7 @@ void Shutdown()
#endif #endif
Reset(); Reset();
s_libusb_driver_not_supported = false; s_status = NO_ADAPTER_DETECTED;
} }
static void Reset() static void Reset()
@ -346,7 +361,7 @@ static void Reset()
std::unique_lock<std::mutex> lock(s_init_mutex, std::defer_lock); std::unique_lock<std::mutex> lock(s_init_mutex, std::defer_lock);
if (!lock.try_lock()) if (!lock.try_lock())
return; return;
if (!s_detected) if (s_status != ADAPTER_DETECTED)
return; return;
if (s_adapter_thread_running.TestAndClear()) if (s_adapter_thread_running.TestAndClear())
@ -359,7 +374,7 @@ static void Reset()
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; i++) for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; i++)
s_controller_type[i] = ControllerTypes::CONTROLLER_NONE; s_controller_type[i] = ControllerTypes::CONTROLLER_NONE;
s_detected = false; s_status = NO_ADAPTER_DETECTED;
if (s_handle) if (s_handle)
{ {
@ -377,7 +392,7 @@ GCPadStatus Input(int chan)
if (!UseAdapter()) if (!UseAdapter())
return {}; return {};
if (s_handle == nullptr || !s_detected) if (s_handle == nullptr || s_status != ADAPTER_DETECTED)
return {}; return {};
int payload_size = 0; int payload_size = 0;
@ -497,7 +512,7 @@ void ResetRumble()
// being called while the libusb state is being reset // being called while the libusb state is being reset
static void ResetRumbleLockNeeded() static void ResetRumbleLockNeeded()
{ {
if (!UseAdapter() || (s_handle == nullptr || !s_detected)) if (!UseAdapter() || (s_handle == nullptr || s_status != ADAPTER_DETECTED))
{ {
return; return;
} }
@ -527,14 +542,20 @@ void Output(int chan, u8 rumble_command)
} }
} }
bool IsDetected() bool IsDetected(const char** error_message)
{ {
return s_detected; if (s_status >= 0)
} {
if (error_message)
*error_message = nullptr;
bool IsDriverDetected() return s_status == ADAPTER_DETECTED;
{ }
return !s_libusb_driver_not_supported;
if (error_message)
*error_message = libusb_strerror(static_cast<libusb_error>(s_status));
return false;
} }
} // end of namespace GCAdapter } // end of namespace GCAdapter

View File

@ -26,8 +26,7 @@ void StartScanThread();
void StopScanThread(); void StopScanThread();
GCPadStatus Input(int chan); GCPadStatus Input(int chan);
void Output(int chan, u8 rumble_command); void Output(int chan, u8 rumble_command);
bool IsDetected(); bool IsDetected(const char** error_message);
bool IsDriverDetected();
bool DeviceConnected(int chan); bool DeviceConnected(int chan);
void ResetDeviceType(int chan); void ResetDeviceType(int chan);
bool UseAdapter(); bool UseAdapter();