USBUtils: Refactor for better structure, merge with BTReal and Host functions

This commit is contained in:
Joshua Vandaële 2025-05-24 05:48:44 +02:00
parent da8610e76f
commit 07a8950b7a
No known key found for this signature in database
GPG Key ID: 5E8F4E7EDBD390EA
14 changed files with 256 additions and 231 deletions

View File

@ -28,6 +28,7 @@
#include "Core/HW/SI/SI_Device.h" #include "Core/HW/SI/SI_Device.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "UICommon/USBUtils.h"
#include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoBackendBase.h"
namespace Config namespace Config
@ -550,40 +551,36 @@ const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII{
const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES{{System::Main, "USBPassthrough", "Devices"}, const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES{{System::Main, "USBPassthrough", "Devices"},
""}; ""};
static std::set<std::pair<u16, u16>> LoadUSBWhitelistFromString(const std::string& devices_string) static std::vector<USBUtils::DeviceInfo>
LoadUSBWhitelistFromString(const std::string& devices_string)
{ {
std::set<std::pair<u16, u16>> devices; std::vector<USBUtils::DeviceInfo> devices;
for (const auto& pair : SplitString(devices_string, ',')) for (const auto& pair : SplitString(devices_string, ','))
{ {
const auto index = pair.find(':'); auto device = USBUtils::DeviceInfo::FromString(pair);
if (index == std::string::npos) if (device)
continue; devices.push_back(*device);
const u16 vid = static_cast<u16>(strtol(pair.substr(0, index).c_str(), nullptr, 16));
const u16 pid = static_cast<u16>(strtol(pair.substr(index + 1).c_str(), nullptr, 16));
if (vid && pid)
devices.emplace(vid, pid);
} }
return devices; return devices;
} }
static std::string SaveUSBWhitelistToString(const std::set<std::pair<u16, u16>>& devices) static std::string SaveUSBWhitelistToString(const std::vector<USBUtils::DeviceInfo>& devices)
{ {
std::ostringstream oss; std::ostringstream oss;
for (const auto& device : devices) for (const auto& device : devices)
oss << fmt::format("{:04x}:{:04x}", device.first, device.second) << ','; oss << device.ToString() << ',';
std::string devices_string = oss.str(); std::string devices_string = oss.str();
if (!devices_string.empty()) if (!devices_string.empty())
devices_string.pop_back(); devices_string.pop_back();
return devices_string; return devices_string;
} }
std::set<std::pair<u16, u16>> GetUSBDeviceWhitelist() std::vector<USBUtils::DeviceInfo> GetUSBDeviceWhitelist()
{ {
return LoadUSBWhitelistFromString(Config::Get(Config::MAIN_USB_PASSTHROUGH_DEVICES)); return LoadUSBWhitelistFromString(Config::Get(Config::MAIN_USB_PASSTHROUGH_DEVICES));
} }
void SetUSBDeviceWhitelist(const std::set<std::pair<u16, u16>>& devices) void SetUSBDeviceWhitelist(const std::vector<USBUtils::DeviceInfo>& devices)
{ {
Config::SetBase(Config::MAIN_USB_PASSTHROUGH_DEVICES, SaveUSBWhitelistToString(devices)); Config::SetBase(Config::MAIN_USB_PASSTHROUGH_DEVICES, SaveUSBWhitelistToString(devices));
} }

View File

@ -12,6 +12,7 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Config/Config.h" #include "Common/Config/Config.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "UICommon/USBUtils.h"
// DSP Backend Types // DSP Backend Types
#define BACKEND_NULLSOUND _trans("No Audio Output") #define BACKEND_NULLSOUND _trans("No Audio Output")
@ -357,8 +358,8 @@ extern const Info<std::string> MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS;
extern const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII; extern const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII;
extern const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES; extern const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES;
std::set<std::pair<u16, u16>> GetUSBDeviceWhitelist(); std::vector<USBUtils::DeviceInfo> GetUSBDeviceWhitelist();
void SetUSBDeviceWhitelist(const std::set<std::pair<u16, u16>>& devices); void SetUSBDeviceWhitelist(const std::vector<USBUtils::DeviceInfo>& devices);
// Main.EmulatedUSBDevices // Main.EmulatedUSBDevices

View File

@ -29,7 +29,6 @@
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/IOS/Device.h" #include "Core/IOS/Device.h"
#include "Core/IOS/USB/Host.h"
#include "Core/System.h" #include "Core/System.h"
#include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/OnScreenDisplay.h"
@ -39,19 +38,6 @@ constexpr u8 REQUEST_TYPE = static_cast<u8>(LIBUSB_ENDPOINT_OUT) |
static_cast<u8>(LIBUSB_REQUEST_TYPE_CLASS) | static_cast<u8>(LIBUSB_REQUEST_TYPE_CLASS) |
static_cast<u8>(LIBUSB_RECIPIENT_INTERFACE); static_cast<u8>(LIBUSB_RECIPIENT_INTERFACE);
static bool IsBluetoothDevice(const libusb_device_descriptor& descriptor)
{
constexpr u8 SUBCLASS = 0x01;
constexpr u8 PROTOCOL_BLUETOOTH = 0x01;
// Some devices misreport their class, so we avoid relying solely on descriptor checks and allow
// users to specify their own VID/PID.
return BluetoothRealDevice::IsConfiguredBluetoothDevice(descriptor.idVendor,
descriptor.idProduct) ||
(descriptor.bDeviceClass == LIBUSB_CLASS_WIRELESS &&
descriptor.bDeviceSubClass == SUBCLASS &&
descriptor.bDeviceProtocol == PROTOCOL_BLUETOOTH);
}
BluetoothRealDevice::BluetoothRealDevice(EmulationKernel& ios, const std::string& device_name) BluetoothRealDevice::BluetoothRealDevice(EmulationKernel& ios, const std::string& device_name)
: BluetoothBaseDevice(ios, device_name) : BluetoothBaseDevice(ios, device_name)
{ {
@ -73,6 +59,19 @@ BluetoothRealDevice::~BluetoothRealDevice()
SaveLinkKeys(); SaveLinkKeys();
} }
bool BluetoothRealDevice::IsBluetoothDevice(const libusb_device_descriptor& descriptor)
{
constexpr u8 SUBCLASS = 0x01;
constexpr u8 PROTOCOL_BLUETOOTH = 0x01;
// Some devices misreport their class, so we avoid relying solely on descriptor checks and allow
// users to specify their own VID/PID.
return BluetoothRealDevice::IsConfiguredBluetoothDevice(descriptor.idVendor,
descriptor.idProduct) ||
(descriptor.bDeviceClass == LIBUSB_CLASS_WIRELESS &&
descriptor.bDeviceSubClass == SUBCLASS &&
descriptor.bDeviceProtocol == PROTOCOL_BLUETOOTH);
}
bool BluetoothRealDevice::IsConfiguredBluetoothDevice(u16 vid, u16 pid) bool BluetoothRealDevice::IsConfiguredBluetoothDevice(u16 vid, u16 pid)
{ {
const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID); const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID);
@ -672,42 +671,6 @@ bool BluetoothRealDevice::OpenDevice(const libusb_device_descriptor& device_desc
return true; return true;
} }
std::vector<BluetoothRealDevice::BluetoothDeviceInfo> BluetoothRealDevice::ListDevices()
{
std::vector<BluetoothDeviceInfo> device_list;
LibusbUtils::Context context;
if (!context.IsValid())
return {};
int result = context.GetDeviceList([&device_list](libusb_device* device) {
libusb_device_descriptor desc;
auto [config_ret, config] = LibusbUtils::MakeConfigDescriptor(device, 0);
if (config_ret != LIBUSB_SUCCESS)
return true;
if (libusb_get_device_descriptor(device, &desc) != LIBUSB_SUCCESS)
return true;
if (IsBluetoothDevice(desc))
{
const std::string device_name =
USBHost::GetDeviceNameFromVIDPID(desc.idVendor, desc.idProduct);
device_list.push_back({desc.idVendor, desc.idProduct, device_name});
}
return true;
});
if (result < 0)
{
ERROR_LOG_FMT(IOS_USB, "Failed to get device list: {}", LibusbUtils::ErrorWrap(result));
return device_list;
}
return device_list;
}
// The callbacks are called from libusb code on a separate thread. // The callbacks are called from libusb code on a separate thread.
void BluetoothRealDevice::HandleCtrlTransfer(libusb_transfer* tr) void BluetoothRealDevice::HandleCtrlTransfer(libusb_transfer* tr)
{ {

View File

@ -59,15 +59,7 @@ public:
void HandleBulkOrIntrTransfer(libusb_transfer* finished_transfer); void HandleBulkOrIntrTransfer(libusb_transfer* finished_transfer);
static bool IsConfiguredBluetoothDevice(u16 vid, u16 pid); static bool IsConfiguredBluetoothDevice(u16 vid, u16 pid);
static bool IsBluetoothDevice(const libusb_device_descriptor& descriptor);
struct BluetoothDeviceInfo
{
u16 vid;
u16 pid;
std::string name;
};
static std::vector<BluetoothDeviceInfo> ListDevices();
private: private:
static constexpr u8 INTERFACE = 0x00; static constexpr u8 INTERFACE = 0x00;

View File

@ -11,14 +11,6 @@
#include <string> #include <string>
#include <utility> #include <utility>
#ifdef __LIBUSB__
#include <libusb.h>
#endif
#ifdef HAVE_LIBUDEV
#include <libudev.h>
#endif
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
@ -53,68 +45,6 @@ std::optional<IPCReply> USBHost::Open(const OpenRequest& request)
return IPCReply(IPC_SUCCESS); return IPCReply(IPC_SUCCESS);
} }
std::string USBHost::GetDeviceNameFromVIDPID(u16 vid, u16 pid)
{
std::string device_name;
#ifdef __LIBUSB__
LibusbUtils::Context context;
if (!context.IsValid())
return device_name;
context.GetDeviceList([&device_name, vid, pid](libusb_device* device) {
libusb_device_descriptor desc;
if (libusb_get_device_descriptor(device, &desc) != LIBUSB_SUCCESS)
return true;
if (desc.idVendor == vid && desc.idProduct == pid)
{
libusb_device_handle* handle;
if (libusb_open(device, &handle) == LIBUSB_SUCCESS)
{
unsigned char buffer[256];
if (desc.iProduct &&
libusb_get_string_descriptor_ascii(handle, desc.iProduct, buffer, sizeof(buffer)) > 0)
{
device_name = reinterpret_cast<char*>(buffer);
libusb_close(handle);
}
}
return false;
}
return true;
});
if (!device_name.empty())
return device_name;
#endif
#ifdef HAVE_LIBUDEV
udev* udev = udev_new();
if (!udev)
return device_name;
udev_hwdb* hwdb = udev_hwdb_new(udev);
if (hwdb)
{
const std::string modalias = fmt::format("usb:v{:04X}p{:04X}*", vid, pid);
udev_list_entry* entries = udev_hwdb_get_properties_list_entry(hwdb, modalias.c_str(), 0);
if (entries)
{
udev_list_entry* device_name_entry =
udev_list_entry_get_by_name(entries, "ID_MODEL_FROM_DATABASE");
if (device_name_entry)
{
device_name = udev_list_entry_get_value(device_name_entry);
}
}
udev_hwdb_unref(hwdb);
}
#endif
return device_name;
}
void USBHost::DoState(PointerWrap& p) void USBHost::DoState(PointerWrap& p)
{ {
Device::DoState(p); Device::DoState(p);

View File

@ -32,7 +32,6 @@ public:
void DoState(PointerWrap& p) override; void DoState(PointerWrap& p) override;
void OnDevicesChanged(const USBScanner::DeviceMap& new_devices); void OnDevicesChanged(const USBScanner::DeviceMap& new_devices);
static std::string GetDeviceNameFromVIDPID(u16 vid, u16 pid);
protected: protected:
enum class ChangeEvent enum class ChangeEvent

View File

@ -28,6 +28,7 @@
#include "Core/IOS/USB/LibusbDevice.h" #include "Core/IOS/USB/LibusbDevice.h"
#include "Core/NetPlayProto.h" #include "Core/NetPlayProto.h"
#include "Core/System.h" #include "Core/System.h"
#include "UICommon/USBUtils.h"
namespace IOS::HLE namespace IOS::HLE
{ {
@ -155,7 +156,9 @@ bool USBScanner::AddNewDevices(DeviceMap* new_devices) const
{ {
WakeupSantrollerDevice(device); WakeupSantrollerDevice(device);
} }
if (!whitelist.contains({descriptor.idVendor, descriptor.idProduct})) const USBUtils::DeviceInfo device_info{descriptor.idVendor, descriptor.idProduct};
const bool not_whitelisted = std::ranges::find(whitelist, device_info) == whitelist.end();
if (not_whitelisted)
return true; return true;
auto usb_device = std::make_unique<USB::LibusbDevice>(device, descriptor); auto usb_device = std::make_unique<USB::LibusbDevice>(device, descriptor);

View File

@ -41,7 +41,7 @@
#include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
#include "UICommon/UICommon.h" #include "UICommon/USBUtils.h"
WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent) WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent)
{ {
@ -70,6 +70,7 @@ void WiimoteControllersWidget::UpdateBluetoothAvailableStatus()
void WiimoteControllersWidget::StartBluetoothAdapterRefresh() void WiimoteControllersWidget::StartBluetoothAdapterRefresh()
{ {
#ifdef __LIBUSB__
if (m_bluetooth_adapter_scan_in_progress) if (m_bluetooth_adapter_scan_in_progress)
return; return;
@ -81,7 +82,7 @@ void WiimoteControllersWidget::StartBluetoothAdapterRefresh()
const auto scan_func = [this]() { const auto scan_func = [this]() {
INFO_LOG_FMT(COMMON, "Refreshing Bluetooth adapter list..."); INFO_LOG_FMT(COMMON, "Refreshing Bluetooth adapter list...");
auto device_list = IOS::HLE::BluetoothRealDevice::ListDevices(); auto device_list = USBUtils::ListDevices(IOS::HLE::BluetoothRealDevice::IsBluetoothDevice);
INFO_LOG_FMT(COMMON, "{} Bluetooth adapters available.", device_list.size()); INFO_LOG_FMT(COMMON, "{} Bluetooth adapters available.", device_list.size());
const auto refresh_complete_func = [this, devices = std::move(device_list)]() { const auto refresh_complete_func = [this, devices = std::move(device_list)]() {
OnBluetoothAdapterRefreshComplete(devices); OnBluetoothAdapterRefreshComplete(devices);
@ -90,10 +91,11 @@ void WiimoteControllersWidget::StartBluetoothAdapterRefresh()
}; };
m_bluetooth_adapter_refresh_thread.Push(scan_func); m_bluetooth_adapter_refresh_thread.Push(scan_func);
#endif
} }
void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete( void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete(
const std::vector<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>& devices) const std::vector<USBUtils::DeviceInfo>& devices)
{ {
const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID); const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID);
const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID); const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID);
@ -109,7 +111,7 @@ void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete(
for (auto& device : devices) for (auto& device : devices)
{ {
std::string name = device.name.empty() ? tr("Unknown Device").toStdString() : device.name; std::string name = device.name ? *device.name : tr("Unknown Device").toStdString();
QString device_info = QString device_info =
QString::fromStdString(fmt::format("{} ({:04x}:{:04x})", name, device.vid, device.pid)); QString::fromStdString(fmt::format("{} ({:04x}:{:04x})", name, device.vid, device.pid));
m_bluetooth_adapters->addItem(device_info, QVariant::fromValue(device)); m_bluetooth_adapters->addItem(device_info, QVariant::fromValue(device));
@ -127,7 +129,7 @@ void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete(
const QString name = QLatin1Char{'['} + tr("disconnected") + QLatin1Char(']'); const QString name = QLatin1Char{'['} + tr("disconnected") + QLatin1Char(']');
const std::string name_str = name.toStdString(); const std::string name_str = name.toStdString();
IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo disconnected_device; USBUtils::DeviceInfo disconnected_device;
disconnected_device.vid = configured_vid; disconnected_device.vid = configured_vid;
disconnected_device.pid = configured_pid; disconnected_device.pid = configured_pid;
disconnected_device.name = name_str; disconnected_device.name = name_str;
@ -315,8 +317,7 @@ void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index)
return; return;
} }
auto device_info = m_bluetooth_adapters->itemData(index) auto device_info = m_bluetooth_adapters->itemData(index).value<USBUtils::DeviceInfo>();
.value<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>();
Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info.pid); Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info.pid);
Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info.vid); Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info.vid);

View File

@ -8,7 +8,7 @@
#include <QWidget> #include <QWidget>
#include "Common/WorkQueueThread.h" #include "Common/WorkQueueThread.h"
#include "Core/IOS/USB/Bluetooth/BTReal.h" #include "UICommon/USBUtils.h"
class QCheckBox; class QCheckBox;
class QComboBox; class QComboBox;
@ -38,8 +38,7 @@ private:
void OnBluetoothPassthroughDeviceChanged(int index); void OnBluetoothPassthroughDeviceChanged(int index);
void OnBluetoothPassthroughSyncPressed(); void OnBluetoothPassthroughSyncPressed();
void OnBluetoothPassthroughResetPressed(); void OnBluetoothPassthroughResetPressed();
void OnBluetoothAdapterRefreshComplete( void OnBluetoothAdapterRefreshComplete(const std::vector<USBUtils::DeviceInfo>& devices);
const std::vector<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>& devices);
void OnWiimoteRefreshPressed(); void OnWiimoteRefreshPressed();
void OnWiimoteConfigure(size_t index); void OnWiimoteConfigure(size_t index);
void StartBluetoothAdapterRefresh(); void StartBluetoothAdapterRefresh();

View File

@ -16,6 +16,8 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
#include <fmt/format.h>
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
@ -101,27 +103,34 @@ void USBDeviceAddToWhitelistDialog::InitControls()
// i18n: PID means Product ID (in the context of a USB device), not Process ID // i18n: PID means Product ID (in the context of a USB device), not Process ID
device_pid_textbox->setPlaceholderText(tr("Device PID (e.g., 0305)")); device_pid_textbox->setPlaceholderText(tr("Device PID (e.g., 0305)"));
} }
void USBDeviceAddToWhitelistDialog::RefreshDeviceList() void USBDeviceAddToWhitelistDialog::RefreshDeviceList()
{ {
const auto& current_devices = USBUtils::GetInsertedDevices(); #ifdef __LIBUSB__
auto whitelist = Config::GetUSBDeviceWhitelist();
const auto& current_devices =
USBUtils::ListDevices([&whitelist](const libusb_device_descriptor& desc) {
return std::ranges::find(whitelist, USBUtils::DeviceInfo{desc.idVendor, desc.idProduct}) ==
whitelist.end();
});
if (current_devices == m_shown_devices) if (current_devices == m_shown_devices)
return; return;
const auto selection_string = usb_inserted_devices_list->currentItem(); const auto selection_string = usb_inserted_devices_list->currentItem();
usb_inserted_devices_list->clear(); usb_inserted_devices_list->clear();
auto whitelist = Config::GetUSBDeviceWhitelist();
for (const auto& device : current_devices) for (const auto& device : current_devices)
{ {
if (whitelist.contains({device.first.first, device.first.second})) const std::string name = device.name ? *device.name : tr("Unknown Device").toStdString();
continue; QString device_text =
usb_inserted_devices_list->addItem(QString::fromStdString(device.second)); QString::fromStdString(fmt::format("{:04x}:{:04x} - {}", device.vid, device.pid, name));
usb_inserted_devices_list->addItem(device_text);
} }
usb_inserted_devices_list->setCurrentItem(selection_string); usb_inserted_devices_list->setCurrentItem(selection_string);
m_shown_devices = current_devices; m_shown_devices = current_devices;
#endif
} }
void USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist() void USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist()
{ {
const std::string vid_string(StripWhitespace(device_vid_textbox->text().toStdString())); const std::string vid_string(StripWhitespace(device_vid_textbox->text().toStdString()));
@ -143,15 +152,16 @@ void USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist()
const u16 vid = static_cast<u16>(std::stoul(vid_string, nullptr, 16)); const u16 vid = static_cast<u16>(std::stoul(vid_string, nullptr, 16));
const u16 pid = static_cast<u16>(std::stoul(pid_string, nullptr, 16)); const u16 pid = static_cast<u16>(std::stoul(pid_string, nullptr, 16));
USBUtils::DeviceInfo new_device{vid, pid};
auto whitelist = Config::GetUSBDeviceWhitelist(); auto whitelist = Config::GetUSBDeviceWhitelist();
auto it = whitelist.emplace(vid, pid); if (std::ranges::find(whitelist, new_device) != whitelist.end())
if (!it.second)
{ {
ModalMessageBox::critical(this, tr("USB Whitelist Error"), ModalMessageBox::critical(this, tr("USB Whitelist Error"),
tr("This USB device is already whitelisted.")); tr("This USB device is already whitelisted."));
return; return;
} }
whitelist.push_back(new_device);
Config::SetUSBDeviceWhitelist(whitelist); Config::SetUSBDeviceWhitelist(whitelist);
Config::Save(); Config::Save();
accept(); accept();

View File

@ -3,8 +3,12 @@
#pragma once #pragma once
#include <vector>
#include <QDialog> #include <QDialog>
#include "UICommon/USBUtils.h"
class QTimer; class QTimer;
class QDialog; class QDialog;
class QHeaderView; class QHeaderView;
@ -44,5 +48,5 @@ private:
void OnDeviceSelection(); void OnDeviceSelection();
std::map<std::pair<quint16, quint16>, std::string> m_shown_devices; std::vector<USBUtils::DeviceInfo> m_shown_devices;
}; };

View File

@ -5,6 +5,7 @@
#include <array> #include <array>
#include <future> #include <future>
#include <optional>
#include <utility> #include <utility>
#include <QCheckBox> #include <QCheckBox>
@ -475,13 +476,17 @@ void WiiPane::OnUSBWhitelistAddButton()
void WiiPane::OnUSBWhitelistRemoveButton() void WiiPane::OnUSBWhitelistRemoveButton()
{ {
QString device = m_whitelist_usb_list->currentItem()->text().left(9); QString device = m_whitelist_usb_list->currentItem()->text().left(9);
QStringList split = device.split(QString::fromStdString(":")); std::optional<USBUtils::DeviceInfo> device_info =
QString vid = QString(split[0]); USBUtils::DeviceInfo::FromString(device.toStdString());
QString pid = QString(split[1]); if (!device_info)
const u16 vid_u16 = static_cast<u16>(std::stoul(vid.toStdString(), nullptr, 16)); {
const u16 pid_u16 = static_cast<u16>(std::stoul(pid.toStdString(), nullptr, 16)); ModalMessageBox::critical(this, tr("USB Whitelist Error"),
tr("Unable to parse the selected device."));
return;
}
auto whitelist = Config::GetUSBDeviceWhitelist(); auto whitelist = Config::GetUSBDeviceWhitelist();
whitelist.erase({vid_u16, pid_u16}); std::erase_if(whitelist,
[device_info](const USBUtils::DeviceInfo& dev) { return dev == *device_info; });
Config::SetUSBDeviceWhitelist(whitelist); Config::SetUSBDeviceWhitelist(whitelist);
PopulateUSBPassthroughListWidget(); PopulateUSBPassthroughListWidget();
} }
@ -492,8 +497,11 @@ void WiiPane::PopulateUSBPassthroughListWidget()
auto whitelist = Config::GetUSBDeviceWhitelist(); auto whitelist = Config::GetUSBDeviceWhitelist();
for (const auto& device : whitelist) for (const auto& device : whitelist)
{ {
QListWidgetItem* usb_lwi = std::optional<std::string> opt_name = USBUtils::GetDeviceNameFromVIDPID(device.vid, device.pid);
new QListWidgetItem(QString::fromStdString(USBUtils::GetDeviceName(device))); const std::string name = opt_name ? *opt_name : tr("Unknown Device").toStdString();
QString device_text =
QString::fromStdString(fmt::format("{:04x}:{:04x} - {}", device.vid, device.pid, name));
QListWidgetItem* usb_lwi = new QListWidgetItem(device_text);
m_whitelist_usb_list->addItem(usb_lwi); m_whitelist_usb_list->addItem(usb_lwi);
} }
ValidateSelectionState(); ValidateSelectionState();

View File

@ -3,81 +3,160 @@
#include "UICommon/USBUtils.h" #include "UICommon/USBUtils.h"
#include <optional>
#include <string_view> #include <string_view>
#include <fmt/format.h> #include <fmt/format.h>
#ifdef __LIBUSB__ #ifdef __LIBUSB__
#include <libusb.h> #include <libusb.h>
#endif #endif
#ifdef HAVE_LIBUDEV
#include <libudev.h>
#endif
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/LibusbUtils.h" #include "Core/LibusbUtils.h"
// Because opening and getting the device name from devices is slow, especially on Windows static const std::vector<USBUtils::DeviceInfo> s_known_devices{{
// with usbdk, we cannot do that for every single device. We should however still show {0x046d, 0x0a03, "Logitech Microphone"},
// device names for known Wii peripherals. {0x057e, 0x0308, "Wii Speak"},
static const std::map<std::pair<u16, u16>, std::string_view> s_known_peripherals{{ {0x057e, 0x0309, "Nintendo USB Microphone"},
{{0x046d, 0x0a03}, "Logitech Microphone"}, {0x057e, 0x030a, "Ubisoft Motion Tracking Camera"},
{{0x057e, 0x0308}, "Wii Speak"}, {0x0e6f, 0x0129, "Disney Infinity Reader (Portal Device)"},
{{0x057e, 0x0309}, "Nintendo USB Microphone"}, {0x12ba, 0x0200, "Harmonix Guitar for PlayStation 3"},
{{0x057e, 0x030a}, "Ubisoft Motion Tracking Camera"}, {0x12ba, 0x0210, "Harmonix Drum Kit for PlayStation 3"},
{{0x0e6f, 0x0129}, "Disney Infinity Reader (Portal Device)"}, {0x12ba, 0x0218, "Harmonix Drum Kit for PlayStation 3"},
{{0x12ba, 0x0200}, "Harmonix Guitar for PlayStation 3"}, {0x12ba, 0x2330, "Harmonix RB3 Keyboard for PlayStation 3"},
{{0x12ba, 0x0210}, "Harmonix Drum Kit for PlayStation 3"}, {0x12ba, 0x2338, "Harmonix RB3 MIDI Keyboard Interface for PlayStation 3"},
{{0x12ba, 0x0218}, "Harmonix Drum Kit for PlayStation 3"}, {0x12ba, 0x2430, "Harmonix RB3 Mustang Guitar for PlayStation 3"},
{{0x12ba, 0x2330}, "Harmonix RB3 Keyboard for PlayStation 3"}, {0x12ba, 0x2438, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"},
{{0x12ba, 0x2338}, "Harmonix RB3 MIDI Keyboard Interface for PlayStation 3"}, {0x12ba, 0x2530, "Harmonix RB3 Squier Guitar for PlayStation 3"},
{{0x12ba, 0x2430}, "Harmonix RB3 Mustang Guitar for PlayStation 3"}, {0x12ba, 0x2538, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"},
{{0x12ba, 0x2438}, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, {0x1430, 0x0100, "Tony Hawk Ride Skateboard"},
{{0x12ba, 0x2530}, "Harmonix RB3 Squier Guitar for PlayStation 3"}, {0x1430, 0x0150, "Skylanders Portal"},
{{0x12ba, 0x2538}, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, {0x1bad, 0x0004, "Harmonix Guitar Controller for Nintendo Wii"},
{{0x1430, 0x0100}, "Tony Hawk Ride Skateboard"}, {0x1bad, 0x0005, "Harmonix Drum Controller for Nintendo Wii"},
{{0x1430, 0x0150}, "Skylanders Portal"}, {0x1bad, 0x3010, "Harmonix Guitar Controller for Nintendo Wii"},
{{0x1bad, 0x0004}, "Harmonix Guitar Controller for Nintendo Wii"}, {0x1bad, 0x3110, "Harmonix Drum Controller for Nintendo Wii"},
{{0x1bad, 0x0005}, "Harmonix Drum Controller for Nintendo Wii"}, {0x1bad, 0x3138, "Harmonix Drum Controller for Nintendo Wii"},
{{0x1bad, 0x3010}, "Harmonix Guitar Controller for Nintendo Wii"}, {0x1bad, 0x3330, "Harmonix RB3 Keyboard for Nintendo Wii"},
{{0x1bad, 0x3110}, "Harmonix Drum Controller for Nintendo Wii"}, {0x1bad, 0x3338, "Harmonix RB3 MIDI Keyboard Interface for Nintendo Wii"},
{{0x1bad, 0x3138}, "Harmonix Drum Controller for Nintendo Wii"}, {0x1bad, 0x3430, "Harmonix RB3 Mustang Guitar for Nintendo Wii"},
{{0x1bad, 0x3330}, "Harmonix RB3 Keyboard for Nintendo Wii"}, {0x1bad, 0x3438, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"},
{{0x1bad, 0x3338}, "Harmonix RB3 MIDI Keyboard Interface for Nintendo Wii"}, {0x1bad, 0x3530, "Harmonix RB3 Squier Guitar for Nintendo Wii"},
{{0x1bad, 0x3430}, "Harmonix RB3 Mustang Guitar for Nintendo Wii"}, {0x1bad, 0x3538, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"},
{{0x1bad, 0x3438}, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"}, {0x21a4, 0xac40, "EA Active NFL"},
{{0x1bad, 0x3530}, "Harmonix RB3 Squier Guitar for Nintendo Wii"},
{{0x1bad, 0x3538}, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"},
{{0x21a4, 0xac40}, "EA Active NFL"},
}}; }};
namespace USBUtils namespace USBUtils
{ {
std::map<std::pair<u16, u16>, std::string> GetInsertedDevices() #ifdef __LIBUSB__
std::vector<DeviceInfo>
ListDevices(const std::function<bool(const libusb_device_descriptor&)>& filter)
{ {
std::map<std::pair<u16, u16>, std::string> devices; std::vector<DeviceInfo> device_list;
LibusbUtils::Context context;
if (!context.IsValid())
return {};
const int ret = context.GetDeviceList([&device_list, &filter](libusb_device* device) {
libusb_device_descriptor desc;
auto [config_ret, config] = LibusbUtils::MakeConfigDescriptor(device, 0);
if (config_ret != LIBUSB_SUCCESS)
return true;
if (libusb_get_device_descriptor(device, &desc) != LIBUSB_SUCCESS)
return true;
if (filter(desc))
{
const std::optional<std::string> name =
GetDeviceNameFromVIDPID(desc.idVendor, desc.idProduct);
device_list.push_back({desc.idVendor, desc.idProduct, name});
}
return true;
});
if (ret < 0)
{
ERROR_LOG_FMT(IOS_USB, "Failed to get device list: {}", LibusbUtils::ErrorWrap(ret));
return {};
}
return device_list;
}
#endif
std::optional<std::string> GetDeviceNameFromVIDPID(u16 vid, u16 pid)
{
std::optional<std::string> device_name;
const auto iter = std::ranges::find_if(s_known_devices, [vid, pid](const DeviceInfo& dev) {
return dev.vid == vid && dev.pid == pid;
});
if (iter != s_known_devices.end())
return iter->name;
#ifdef __LIBUSB__ #ifdef __LIBUSB__
LibusbUtils::Context context; LibusbUtils::Context context;
if (!context.IsValid())
return devices;
const int ret = context.GetDeviceList([&](libusb_device* device) { if (context.IsValid())
libusb_device_descriptor descr; {
libusb_get_device_descriptor(device, &descr); context.GetDeviceList([&device_name, vid, pid](libusb_device* device) {
const std::pair<u16, u16> vid_pid{descr.idVendor, descr.idProduct}; libusb_device_descriptor desc;
devices[vid_pid] = GetDeviceName(vid_pid);
return true; if (libusb_get_device_descriptor(device, &desc) != LIBUSB_SUCCESS)
}); return true;
if (ret != LIBUSB_SUCCESS)
WARN_LOG_FMT(COMMON, "GetDeviceList failed: {}", LibusbUtils::ErrorWrap(ret)); if (desc.idVendor == vid && desc.idProduct == pid)
{
libusb_device_handle* handle;
if (libusb_open(device, &handle) == LIBUSB_SUCCESS)
{
unsigned char buffer[256];
if (desc.iProduct &&
libusb_get_string_descriptor_ascii(handle, desc.iProduct, buffer, sizeof(buffer)) > 0)
{
device_name = reinterpret_cast<char*>(buffer);
libusb_close(handle);
}
}
return false;
}
return true;
});
}
if (device_name)
return device_name;
#endif #endif
#ifdef HAVE_LIBUDEV
udev* udev = udev_new();
if (!udev)
return device_name;
return devices; udev_hwdb* hwdb = udev_hwdb_new(udev);
} if (!hwdb)
return device_name;
std::string GetDeviceName(const std::pair<u16, u16> vid_pid) const std::string modalias = fmt::format("usb:v{:04X}p{:04X}*", vid, pid);
{ udev_list_entry* entries = udev_hwdb_get_properties_list_entry(hwdb, modalias.c_str(), 0);
const auto iter = s_known_peripherals.find(vid_pid);
const std::string_view device_name = if (entries)
iter == s_known_peripherals.cend() ? "Unknown" : iter->second; {
return fmt::format("{:04x}:{:04x} - {}", vid_pid.first, vid_pid.second, device_name); udev_list_entry* device_name_entry =
udev_list_entry_get_by_name(entries, "ID_MODEL_FROM_DATABASE");
if (device_name_entry)
{
device_name = udev_list_entry_get_value(device_name_entry);
}
}
udev_hwdb_unref(hwdb);
#endif
return device_name;
} }
} // namespace USBUtils } // namespace USBUtils

View File

@ -3,14 +3,53 @@
#pragma once #pragma once
#include <functional>
#include <map> #include <map>
#include <optional>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector>
#ifdef __LIBUSB__
#include <libusb.h>
#endif
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
namespace USBUtils namespace USBUtils
{ {
std::map<std::pair<u16, u16>, std::string> GetInsertedDevices();
std::string GetDeviceName(std::pair<u16, u16> vid_pid); struct DeviceInfo
{
u16 vid;
u16 pid;
std::optional<std::string> name;
bool operator==(const DeviceInfo& other) const { return vid == other.vid && pid == other.pid; }
static std::optional<DeviceInfo> FromString(const std::string& str)
{
const auto index = str.find(':');
if (index == std::string::npos)
return std::nullopt;
u16 vid = static_cast<u16>(strtol(str.substr(0, index).c_str(), nullptr, 16));
u16 pid = static_cast<u16>(strtol(str.substr(index + 1).c_str(), nullptr, 16));
if (vid && pid)
return DeviceInfo{vid, pid, std::nullopt};
return std::nullopt;
}
std::string ToString() const
{
char buf[10];
snprintf(buf, sizeof(buf), "%04x:%04x", vid, pid);
return std::string(buf);
}
};
#ifdef __LIBUSB__
std::vector<DeviceInfo>
ListDevices(const std::function<bool(const libusb_device_descriptor&)>& filter =
[](const libusb_device_descriptor&) { return true; });
#endif
std::optional<std::string> GetDeviceNameFromVIDPID(u16 vid, u16 pid);
} // namespace USBUtils } // namespace USBUtils