diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 5577fba8c8..18081889d4 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -28,6 +28,7 @@ #include "Core/HW/SI/SI_Device.h" #include "Core/PowerPC/PowerPC.h" #include "DiscIO/Enums.h" +#include "UICommon/USBUtils.h" #include "VideoCommon/VideoBackendBase.h" namespace Config @@ -550,40 +551,36 @@ const Info MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII{ const Info MAIN_USB_PASSTHROUGH_DEVICES{{System::Main, "USBPassthrough", "Devices"}, ""}; -static std::set> LoadUSBWhitelistFromString(const std::string& devices_string) +static std::vector +LoadUSBWhitelistFromString(const std::string& devices_string) { - std::set> devices; + std::vector devices; for (const auto& pair : SplitString(devices_string, ',')) { - const auto index = pair.find(':'); - if (index == std::string::npos) - continue; - - const u16 vid = static_cast(strtol(pair.substr(0, index).c_str(), nullptr, 16)); - const u16 pid = static_cast(strtol(pair.substr(index + 1).c_str(), nullptr, 16)); - if (vid && pid) - devices.emplace(vid, pid); + auto device = USBUtils::DeviceInfo::FromString(pair); + if (device) + devices.push_back(*device); } return devices; } -static std::string SaveUSBWhitelistToString(const std::set>& devices) +static std::string SaveUSBWhitelistToString(const std::vector& devices) { std::ostringstream oss; for (const auto& device : devices) - oss << fmt::format("{:04x}:{:04x}", device.first, device.second) << ','; + oss << device.ToString() << ','; std::string devices_string = oss.str(); if (!devices_string.empty()) devices_string.pop_back(); return devices_string; } -std::set> GetUSBDeviceWhitelist() +std::vector GetUSBDeviceWhitelist() { return LoadUSBWhitelistFromString(Config::Get(Config::MAIN_USB_PASSTHROUGH_DEVICES)); } -void SetUSBDeviceWhitelist(const std::set>& devices) +void SetUSBDeviceWhitelist(const std::vector& devices) { Config::SetBase(Config::MAIN_USB_PASSTHROUGH_DEVICES, SaveUSBWhitelistToString(devices)); } diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 4e3ad8830f..cdd3eb6488 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -12,6 +12,7 @@ #include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "DiscIO/Enums.h" +#include "UICommon/USBUtils.h" // DSP Backend Types #define BACKEND_NULLSOUND _trans("No Audio Output") @@ -357,8 +358,8 @@ extern const Info MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS; extern const Info MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII; extern const Info MAIN_USB_PASSTHROUGH_DEVICES; -std::set> GetUSBDeviceWhitelist(); -void SetUSBDeviceWhitelist(const std::set>& devices); +std::vector GetUSBDeviceWhitelist(); +void SetUSBDeviceWhitelist(const std::vector& devices); // Main.EmulatedUSBDevices diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp index fae0c6e662..23f4b17f24 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp @@ -29,7 +29,6 @@ #include "Core/Core.h" #include "Core/HW/Memmap.h" #include "Core/IOS/Device.h" -#include "Core/IOS/USB/Host.h" #include "Core/System.h" #include "VideoCommon/OnScreenDisplay.h" @@ -39,19 +38,6 @@ constexpr u8 REQUEST_TYPE = static_cast(LIBUSB_ENDPOINT_OUT) | static_cast(LIBUSB_REQUEST_TYPE_CLASS) | static_cast(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) : BluetoothBaseDevice(ios, device_name) { @@ -73,6 +59,19 @@ BluetoothRealDevice::~BluetoothRealDevice() 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) { 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; } -std::vector BluetoothRealDevice::ListDevices() -{ - std::vector 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. void BluetoothRealDevice::HandleCtrlTransfer(libusb_transfer* tr) { diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h index 96a18c0617..072c02c085 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h @@ -59,15 +59,7 @@ public: void HandleBulkOrIntrTransfer(libusb_transfer* finished_transfer); static bool IsConfiguredBluetoothDevice(u16 vid, u16 pid); - - struct BluetoothDeviceInfo - { - u16 vid; - u16 pid; - std::string name; - }; - - static std::vector ListDevices(); + static bool IsBluetoothDevice(const libusb_device_descriptor& descriptor); private: static constexpr u8 INTERFACE = 0x00; diff --git a/Source/Core/Core/IOS/USB/Host.cpp b/Source/Core/Core/IOS/USB/Host.cpp index 03598f0d00..e85c3a52f6 100644 --- a/Source/Core/Core/IOS/USB/Host.cpp +++ b/Source/Core/Core/IOS/USB/Host.cpp @@ -11,14 +11,6 @@ #include #include -#ifdef __LIBUSB__ -#include -#endif - -#ifdef HAVE_LIBUDEV -#include -#endif - #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" @@ -53,68 +45,6 @@ std::optional USBHost::Open(const OpenRequest& request) 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(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) { Device::DoState(p); diff --git a/Source/Core/Core/IOS/USB/Host.h b/Source/Core/Core/IOS/USB/Host.h index e457c2bc5b..6c1c488ee9 100644 --- a/Source/Core/Core/IOS/USB/Host.h +++ b/Source/Core/Core/IOS/USB/Host.h @@ -32,7 +32,6 @@ public: void DoState(PointerWrap& p) override; void OnDevicesChanged(const USBScanner::DeviceMap& new_devices); - static std::string GetDeviceNameFromVIDPID(u16 vid, u16 pid); protected: enum class ChangeEvent diff --git a/Source/Core/Core/IOS/USB/USBScanner.cpp b/Source/Core/Core/IOS/USB/USBScanner.cpp index b0a170ee35..d246205719 100644 --- a/Source/Core/Core/IOS/USB/USBScanner.cpp +++ b/Source/Core/Core/IOS/USB/USBScanner.cpp @@ -28,6 +28,7 @@ #include "Core/IOS/USB/LibusbDevice.h" #include "Core/NetPlayProto.h" #include "Core/System.h" +#include "UICommon/USBUtils.h" namespace IOS::HLE { @@ -155,7 +156,9 @@ bool USBScanner::AddNewDevices(DeviceMap* new_devices) const { 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; auto usb_device = std::make_unique(device, descriptor); diff --git a/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp b/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp index 960cfac080..4e2e588a5a 100644 --- a/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp +++ b/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp @@ -41,7 +41,7 @@ #include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/Settings.h" -#include "UICommon/UICommon.h" +#include "UICommon/USBUtils.h" WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent) { @@ -70,6 +70,7 @@ void WiimoteControllersWidget::UpdateBluetoothAvailableStatus() void WiimoteControllersWidget::StartBluetoothAdapterRefresh() { +#ifdef __LIBUSB__ if (m_bluetooth_adapter_scan_in_progress) return; @@ -81,7 +82,7 @@ void WiimoteControllersWidget::StartBluetoothAdapterRefresh() const auto scan_func = [this]() { 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()); const auto refresh_complete_func = [this, devices = std::move(device_list)]() { OnBluetoothAdapterRefreshComplete(devices); @@ -90,10 +91,11 @@ void WiimoteControllersWidget::StartBluetoothAdapterRefresh() }; m_bluetooth_adapter_refresh_thread.Push(scan_func); +#endif } void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete( - const std::vector& devices) + const std::vector& devices) { const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID); const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID); @@ -109,7 +111,7 @@ void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete( 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::fromStdString(fmt::format("{} ({:04x}:{:04x})", name, device.vid, device.pid)); m_bluetooth_adapters->addItem(device_info, QVariant::fromValue(device)); @@ -127,7 +129,7 @@ void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete( const QString name = QLatin1Char{'['} + tr("disconnected") + QLatin1Char(']'); const std::string name_str = name.toStdString(); - IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo disconnected_device; + USBUtils::DeviceInfo disconnected_device; disconnected_device.vid = configured_vid; disconnected_device.pid = configured_pid; disconnected_device.name = name_str; @@ -315,8 +317,7 @@ void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index) return; } - auto device_info = m_bluetooth_adapters->itemData(index) - .value(); + auto device_info = m_bluetooth_adapters->itemData(index).value(); Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info.pid); Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info.vid); diff --git a/Source/Core/DolphinQt/Config/WiimoteControllersWidget.h b/Source/Core/DolphinQt/Config/WiimoteControllersWidget.h index b174c1b786..47091f8250 100644 --- a/Source/Core/DolphinQt/Config/WiimoteControllersWidget.h +++ b/Source/Core/DolphinQt/Config/WiimoteControllersWidget.h @@ -8,7 +8,7 @@ #include #include "Common/WorkQueueThread.h" -#include "Core/IOS/USB/Bluetooth/BTReal.h" +#include "UICommon/USBUtils.h" class QCheckBox; class QComboBox; @@ -38,8 +38,7 @@ private: void OnBluetoothPassthroughDeviceChanged(int index); void OnBluetoothPassthroughSyncPressed(); void OnBluetoothPassthroughResetPressed(); - void OnBluetoothAdapterRefreshComplete( - const std::vector& devices); + void OnBluetoothAdapterRefreshComplete(const std::vector& devices); void OnWiimoteRefreshPressed(); void OnWiimoteConfigure(size_t index); void StartBluetoothAdapterRefresh(); diff --git a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.cpp b/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.cpp index cf159e0a16..b7d8993a9b 100644 --- a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.cpp +++ b/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include "Common/StringUtil.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 device_pid_textbox->setPlaceholderText(tr("Device PID (e.g., 0305)")); } - 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) return; const auto selection_string = usb_inserted_devices_list->currentItem(); usb_inserted_devices_list->clear(); - auto whitelist = Config::GetUSBDeviceWhitelist(); for (const auto& device : current_devices) { - if (whitelist.contains({device.first.first, device.first.second})) - continue; - usb_inserted_devices_list->addItem(QString::fromStdString(device.second)); + const std::string name = device.name ? *device.name : tr("Unknown Device").toStdString(); + QString device_text = + 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); m_shown_devices = current_devices; +#endif } - void USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist() { const std::string vid_string(StripWhitespace(device_vid_textbox->text().toStdString())); @@ -143,15 +152,16 @@ void USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist() const u16 vid = static_cast(std::stoul(vid_string, nullptr, 16)); const u16 pid = static_cast(std::stoul(pid_string, nullptr, 16)); + USBUtils::DeviceInfo new_device{vid, pid}; auto whitelist = Config::GetUSBDeviceWhitelist(); - auto it = whitelist.emplace(vid, pid); - if (!it.second) + if (std::ranges::find(whitelist, new_device) != whitelist.end()) { ModalMessageBox::critical(this, tr("USB Whitelist Error"), tr("This USB device is already whitelisted.")); return; } + whitelist.push_back(new_device); Config::SetUSBDeviceWhitelist(whitelist); Config::Save(); accept(); diff --git a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h b/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h index 5ae1199f2e..6cb81aca2e 100644 --- a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h +++ b/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h @@ -3,8 +3,12 @@ #pragma once +#include + #include +#include "UICommon/USBUtils.h" + class QTimer; class QDialog; class QHeaderView; @@ -44,5 +48,5 @@ private: void OnDeviceSelection(); - std::map, std::string> m_shown_devices; + std::vector m_shown_devices; }; diff --git a/Source/Core/DolphinQt/Settings/WiiPane.cpp b/Source/Core/DolphinQt/Settings/WiiPane.cpp index 5d6bc23f7e..c810c2956d 100644 --- a/Source/Core/DolphinQt/Settings/WiiPane.cpp +++ b/Source/Core/DolphinQt/Settings/WiiPane.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -475,13 +476,17 @@ void WiiPane::OnUSBWhitelistAddButton() void WiiPane::OnUSBWhitelistRemoveButton() { QString device = m_whitelist_usb_list->currentItem()->text().left(9); - QStringList split = device.split(QString::fromStdString(":")); - QString vid = QString(split[0]); - QString pid = QString(split[1]); - const u16 vid_u16 = static_cast(std::stoul(vid.toStdString(), nullptr, 16)); - const u16 pid_u16 = static_cast(std::stoul(pid.toStdString(), nullptr, 16)); + std::optional device_info = + USBUtils::DeviceInfo::FromString(device.toStdString()); + if (!device_info) + { + ModalMessageBox::critical(this, tr("USB Whitelist Error"), + tr("Unable to parse the selected device.")); + return; + } 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); PopulateUSBPassthroughListWidget(); } @@ -492,8 +497,11 @@ void WiiPane::PopulateUSBPassthroughListWidget() auto whitelist = Config::GetUSBDeviceWhitelist(); for (const auto& device : whitelist) { - QListWidgetItem* usb_lwi = - new QListWidgetItem(QString::fromStdString(USBUtils::GetDeviceName(device))); + std::optional opt_name = USBUtils::GetDeviceNameFromVIDPID(device.vid, device.pid); + 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); } ValidateSelectionState(); diff --git a/Source/Core/UICommon/USBUtils.cpp b/Source/Core/UICommon/USBUtils.cpp index 42c4bd93fd..90192f219e 100644 --- a/Source/Core/UICommon/USBUtils.cpp +++ b/Source/Core/UICommon/USBUtils.cpp @@ -3,81 +3,160 @@ #include "UICommon/USBUtils.h" +#include #include #include #ifdef __LIBUSB__ #include #endif +#ifdef HAVE_LIBUDEV +#include +#endif #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Core/LibusbUtils.h" -// Because opening and getting the device name from devices is slow, especially on Windows -// with usbdk, we cannot do that for every single device. We should however still show -// device names for known Wii peripherals. -static const std::map, std::string_view> s_known_peripherals{{ - {{0x046d, 0x0a03}, "Logitech Microphone"}, - {{0x057e, 0x0308}, "Wii Speak"}, - {{0x057e, 0x0309}, "Nintendo USB Microphone"}, - {{0x057e, 0x030a}, "Ubisoft Motion Tracking Camera"}, - {{0x0e6f, 0x0129}, "Disney Infinity Reader (Portal Device)"}, - {{0x12ba, 0x0200}, "Harmonix Guitar for PlayStation 3"}, - {{0x12ba, 0x0210}, "Harmonix Drum Kit for PlayStation 3"}, - {{0x12ba, 0x0218}, "Harmonix Drum Kit for PlayStation 3"}, - {{0x12ba, 0x2330}, "Harmonix RB3 Keyboard for PlayStation 3"}, - {{0x12ba, 0x2338}, "Harmonix RB3 MIDI Keyboard Interface for PlayStation 3"}, - {{0x12ba, 0x2430}, "Harmonix RB3 Mustang Guitar for PlayStation 3"}, - {{0x12ba, 0x2438}, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, - {{0x12ba, 0x2530}, "Harmonix RB3 Squier Guitar for PlayStation 3"}, - {{0x12ba, 0x2538}, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, - {{0x1430, 0x0100}, "Tony Hawk Ride Skateboard"}, - {{0x1430, 0x0150}, "Skylanders Portal"}, - {{0x1bad, 0x0004}, "Harmonix Guitar Controller for Nintendo Wii"}, - {{0x1bad, 0x0005}, "Harmonix Drum Controller for Nintendo Wii"}, - {{0x1bad, 0x3010}, "Harmonix Guitar Controller for Nintendo Wii"}, - {{0x1bad, 0x3110}, "Harmonix Drum Controller for Nintendo Wii"}, - {{0x1bad, 0x3138}, "Harmonix Drum Controller for Nintendo Wii"}, - {{0x1bad, 0x3330}, "Harmonix RB3 Keyboard for Nintendo Wii"}, - {{0x1bad, 0x3338}, "Harmonix RB3 MIDI Keyboard Interface for Nintendo Wii"}, - {{0x1bad, 0x3430}, "Harmonix RB3 Mustang Guitar for Nintendo Wii"}, - {{0x1bad, 0x3438}, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"}, - {{0x1bad, 0x3530}, "Harmonix RB3 Squier Guitar for Nintendo Wii"}, - {{0x1bad, 0x3538}, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"}, - {{0x21a4, 0xac40}, "EA Active NFL"}, +static const std::vector s_known_devices{{ + {0x046d, 0x0a03, "Logitech Microphone"}, + {0x057e, 0x0308, "Wii Speak"}, + {0x057e, 0x0309, "Nintendo USB Microphone"}, + {0x057e, 0x030a, "Ubisoft Motion Tracking Camera"}, + {0x0e6f, 0x0129, "Disney Infinity Reader (Portal Device)"}, + {0x12ba, 0x0200, "Harmonix Guitar for PlayStation 3"}, + {0x12ba, 0x0210, "Harmonix Drum Kit for PlayStation 3"}, + {0x12ba, 0x0218, "Harmonix Drum Kit for PlayStation 3"}, + {0x12ba, 0x2330, "Harmonix RB3 Keyboard for PlayStation 3"}, + {0x12ba, 0x2338, "Harmonix RB3 MIDI Keyboard Interface for PlayStation 3"}, + {0x12ba, 0x2430, "Harmonix RB3 Mustang Guitar for PlayStation 3"}, + {0x12ba, 0x2438, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, + {0x12ba, 0x2530, "Harmonix RB3 Squier Guitar for PlayStation 3"}, + {0x12ba, 0x2538, "Harmonix RB3 MIDI Guitar Interface for PlayStation 3"}, + {0x1430, 0x0100, "Tony Hawk Ride Skateboard"}, + {0x1430, 0x0150, "Skylanders Portal"}, + {0x1bad, 0x0004, "Harmonix Guitar Controller for Nintendo Wii"}, + {0x1bad, 0x0005, "Harmonix Drum Controller for Nintendo Wii"}, + {0x1bad, 0x3010, "Harmonix Guitar Controller for Nintendo Wii"}, + {0x1bad, 0x3110, "Harmonix Drum Controller for Nintendo Wii"}, + {0x1bad, 0x3138, "Harmonix Drum Controller for Nintendo Wii"}, + {0x1bad, 0x3330, "Harmonix RB3 Keyboard for Nintendo Wii"}, + {0x1bad, 0x3338, "Harmonix RB3 MIDI Keyboard Interface for Nintendo Wii"}, + {0x1bad, 0x3430, "Harmonix RB3 Mustang Guitar for Nintendo Wii"}, + {0x1bad, 0x3438, "Harmonix RB3 MIDI Guitar Interface for Nintendo Wii"}, + {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 { -std::map, std::string> GetInsertedDevices() +#ifdef __LIBUSB__ +std::vector +ListDevices(const std::function& filter) { - std::map, std::string> devices; + std::vector 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 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 GetDeviceNameFromVIDPID(u16 vid, u16 pid) +{ + std::optional 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__ LibusbUtils::Context context; - if (!context.IsValid()) - return devices; - const int ret = context.GetDeviceList([&](libusb_device* device) { - libusb_device_descriptor descr; - libusb_get_device_descriptor(device, &descr); - const std::pair vid_pid{descr.idVendor, descr.idProduct}; - devices[vid_pid] = GetDeviceName(vid_pid); - return true; - }); - if (ret != LIBUSB_SUCCESS) - WARN_LOG_FMT(COMMON, "GetDeviceList failed: {}", LibusbUtils::ErrorWrap(ret)); + if (context.IsValid()) + { + 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(buffer); + libusb_close(handle); + } + } + return false; + } + return true; + }); + } + + if (device_name) + return device_name; #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 vid_pid) -{ - const auto iter = s_known_peripherals.find(vid_pid); - const std::string_view device_name = - iter == s_known_peripherals.cend() ? "Unknown" : iter->second; - return fmt::format("{:04x}:{:04x} - {}", vid_pid.first, vid_pid.second, device_name); + 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; } } // namespace USBUtils diff --git a/Source/Core/UICommon/USBUtils.h b/Source/Core/UICommon/USBUtils.h index 1eb54d81d9..31021a2c77 100644 --- a/Source/Core/UICommon/USBUtils.h +++ b/Source/Core/UICommon/USBUtils.h @@ -3,14 +3,53 @@ #pragma once +#include #include +#include #include #include +#include + +#ifdef __LIBUSB__ +#include +#endif #include "Common/CommonTypes.h" - namespace USBUtils { -std::map, std::string> GetInsertedDevices(); -std::string GetDeviceName(std::pair vid_pid); + +struct DeviceInfo +{ + u16 vid; + u16 pid; + std::optional name; + + bool operator==(const DeviceInfo& other) const { return vid == other.vid && pid == other.pid; } + + static std::optional FromString(const std::string& str) + { + const auto index = str.find(':'); + if (index == std::string::npos) + return std::nullopt; + u16 vid = static_cast(strtol(str.substr(0, index).c_str(), nullptr, 16)); + u16 pid = static_cast(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 +ListDevices(const std::function& filter = + [](const libusb_device_descriptor&) { return true; }); +#endif +std::optional GetDeviceNameFromVIDPID(u16 vid, u16 pid); } // namespace USBUtils