diff --git a/Source/Core/Core/IOS/USB/Host.cpp b/Source/Core/Core/IOS/USB/Host.cpp index 605b03817b..e66b08c646 100644 --- a/Source/Core/Core/IOS/USB/Host.cpp +++ b/Source/Core/Core/IOS/USB/Host.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" @@ -37,19 +38,12 @@ std::optional USBHost::Open(const OpenRequest& request) // Force a device scan to complete, because some games (including Your Shape) only care // about the initial device list (in the first GETDEVICECHANGE reply). m_usb_scanner.WaitForFirstScan(); + OnDevicesChangedInternal(m_usb_scanner.GetDevices()); m_has_initialised = true; } return IPCReply(IPC_SUCCESS); } -void USBHost::UpdateWantDeterminism(const bool new_want_determinism) -{ - if (new_want_determinism) - m_usb_scanner.Stop(); - else if (IsOpened()) - m_usb_scanner.Start(); -} - void USBHost::DoState(PointerWrap& p) { Device::DoState(p); @@ -57,7 +51,9 @@ void USBHost::DoState(PointerWrap& p) { // After a state has loaded, there may be insertion hooks for devices that were // already plugged in, and which need to be triggered. - m_usb_scanner.UpdateDevices(true); + std::lock_guard lk(m_devices_mutex); + m_devices.clear(); + OnDevicesChanged(m_usb_scanner.GetDevices()); } } @@ -86,28 +82,54 @@ bool USBHost::ShouldAddDevice(const USB::Device& device) const void USBHost::Update() { if (Core::WantsDeterminism()) - m_usb_scanner.UpdateDevices(); + OnDevicesChangedInternal(m_usb_scanner.GetDevices()); } -void USBHost::DispatchHooks(const DeviceChangeHooks& hooks) +void USBHost::OnDevicesChanged(const USBScanner::DeviceMap& new_devices) +{ + if (!Core::WantsDeterminism()) + OnDevicesChangedInternal(new_devices); +} + +void USBHost::OnDevicesChangedInternal(const USBScanner::DeviceMap& new_devices) { std::lock_guard lk(m_devices_mutex); - for (const auto& [device, event] : hooks) + bool changes = false; + + for (auto it = m_devices.begin(); it != m_devices.end();) { - INFO_LOG_FMT(IOS_USB, "{} - {} device: {:04x}:{:04x}", GetDeviceName(), - event == ChangeEvent::Inserted ? "New" : "Removed", device->GetVid(), - device->GetPid()); + const auto& [id, device] = *it; + if (!new_devices.contains(id)) + { + INFO_LOG_FMT(IOS_USB, "{} - Removed device: {:04x}:{:04x}", GetDeviceName(), device->GetVid(), + device->GetPid()); - if (event == ChangeEvent::Inserted) - m_devices.emplace(device->GetId(), device); - else if (event == ChangeEvent::Removed) - m_devices.erase(device->GetId()); - - OnDeviceChange(event, device); + changes = true; + auto device_copy = std::move(device); + it = m_devices.erase(it); + OnDeviceChange(ChangeEvent::Removed, std::move(device_copy)); + } + else + { + ++it; + } } - if (!hooks.empty()) + for (const auto& [id, device] : new_devices) + { + if (!m_devices.contains(id)) + { + INFO_LOG_FMT(IOS_USB, "{} - New device: {:04x}:{:04x}", GetDeviceName(), device->GetVid(), + device->GetPid()); + + changes = true; + m_devices.emplace(id, device); + OnDeviceChange(ChangeEvent::Inserted, device); + } + } + + if (changes) OnDeviceChangeEnd(); } diff --git a/Source/Core/Core/IOS/USB/Host.h b/Source/Core/Core/IOS/USB/Host.h index ab4e9e8c6d..92da3e911b 100644 --- a/Source/Core/Core/IOS/USB/Host.h +++ b/Source/Core/Core/IOS/USB/Host.h @@ -29,16 +29,19 @@ public: std::optional Open(const OpenRequest& request) override; - void UpdateWantDeterminism(bool new_want_determinism) override; void DoState(PointerWrap& p) override; virtual bool ShouldAddDevice(const USB::Device& device) const; - void DispatchHooks(const USBScanner::DeviceChangeHooks& hooks); + void OnDevicesChanged(const USBScanner::DeviceMap& new_devices); protected: - using ChangeEvent = USBScanner::ChangeEvent; - using DeviceChangeHooks = USBScanner::DeviceChangeHooks; + enum class ChangeEvent + { + Inserted, + Removed, + }; + using DeviceChangeHooks = std::map, ChangeEvent>; std::shared_ptr GetDeviceById(u64 device_id) const; virtual void OnDeviceChange(ChangeEvent event, std::shared_ptr changed_device); @@ -54,6 +57,7 @@ protected: private: void Update() override; + void OnDevicesChangedInternal(const USBScanner::DeviceMap& new_devices); bool m_has_initialised = false; }; diff --git a/Source/Core/Core/IOS/USB/USBScanner.cpp b/Source/Core/Core/IOS/USB/USBScanner.cpp index 904eeb672d..be0d1c02df 100644 --- a/Source/Core/Core/IOS/USB/USBScanner.cpp +++ b/Source/Core/Core/IOS/USB/USBScanner.cpp @@ -3,8 +3,10 @@ #include "Core/IOS/USB/USBScanner.h" +#include #include #include +#include #include #include #include @@ -47,11 +49,6 @@ void USBScanner::WaitForFirstScan() void USBScanner::Start() { - if (Core::WantsDeterminism()) - { - UpdateDevices(); - return; - } if (m_thread_running.TestAndSet()) { m_thread = std::thread([this] { @@ -70,40 +67,34 @@ void USBScanner::Stop() { if (m_thread_running.TestAndClear()) m_thread.join(); +} - // Clear all devices and dispatch removal hooks. - DeviceChangeHooks hooks; - DetectRemovedDevices(std::set(), hooks); - m_host->DispatchHooks(hooks); +USBScanner::DeviceMap USBScanner::GetDevices() const +{ + std::lock_guard lk(m_devices_mutex); + return m_devices; } // This is called from the scan thread. Returns false if we failed to update the device list. -bool USBScanner::UpdateDevices(const bool always_add_hooks) +bool USBScanner::UpdateDevices() { - DeviceChangeHooks hooks; - std::set plugged_devices; - // If we failed to get a new, up-to-date list of devices, we cannot detect device removals. - if (!AddNewDevices(plugged_devices, hooks, always_add_hooks)) + DeviceMap new_devices; + if (!AddNewDevices(&new_devices)) return false; - DetectRemovedDevices(plugged_devices, hooks); - m_host->DispatchHooks(hooks); - return true; -} -bool USBScanner::AddDevice(std::unique_ptr device) -{ std::lock_guard lk(m_devices_mutex); - if (m_devices.contains(device->GetId())) - return false; + if (!std::ranges::equal(std::views::keys(m_devices), std::views::keys(new_devices))) + { + m_devices = std::move(new_devices); + m_host->OnDevicesChanged(m_devices); + } - m_devices[device->GetId()] = std::move(device); return true; } -bool USBScanner::AddNewDevices(std::set& new_devices, DeviceChangeHooks& hooks, - const bool always_add_hooks) +bool USBScanner::AddNewDevices(DeviceMap* new_devices) const { - AddEmulatedDevices(new_devices, hooks, always_add_hooks); + AddEmulatedDevices(new_devices); #ifdef __LIBUSB__ if (!Core::WantsDeterminism()) { @@ -121,7 +112,7 @@ bool USBScanner::AddNewDevices(std::set& new_devices, DeviceChangeHooks& ho auto usb_device = std::make_unique(m_host->GetEmulationKernel(), device, descriptor); - CheckAndAddDevice(std::move(usb_device), new_devices, hooks, always_add_hooks); + AddDevice(std::move(usb_device), new_devices); return true; }); if (ret != LIBUSB_SUCCESS) @@ -132,60 +123,24 @@ bool USBScanner::AddNewDevices(std::set& new_devices, DeviceChangeHooks& ho return true; } -void USBScanner::DetectRemovedDevices(const std::set& plugged_devices, - DeviceChangeHooks& hooks) -{ - std::lock_guard lk(m_devices_mutex); - for (auto it = m_devices.begin(); it != m_devices.end();) - { - if (!plugged_devices.contains(it->second->GetId())) - { - hooks.emplace(it->second, ChangeEvent::Removed); - it = m_devices.erase(it); - } - else - { - ++it; - } - } -} - -void USBScanner::AddEmulatedDevices(std::set& new_devices, DeviceChangeHooks& hooks, - bool always_add_hooks) +void USBScanner::AddEmulatedDevices(DeviceMap* new_devices) const { if (Config::Get(Config::MAIN_EMULATE_SKYLANDER_PORTAL) && !NetPlay::IsNetPlayRunning()) { auto skylanderportal = std::make_unique(m_host->GetEmulationKernel()); - CheckAndAddDevice(std::move(skylanderportal), new_devices, hooks, always_add_hooks); + AddDevice(std::move(skylanderportal), new_devices); } if (Config::Get(Config::MAIN_EMULATE_INFINITY_BASE) && !NetPlay::IsNetPlayRunning()) { auto infinity_base = std::make_unique(m_host->GetEmulationKernel()); - CheckAndAddDevice(std::move(infinity_base), new_devices, hooks, always_add_hooks); + AddDevice(std::move(infinity_base), new_devices); } } -void USBScanner::CheckAndAddDevice(std::unique_ptr device, std::set& new_devices, - DeviceChangeHooks& hooks, bool always_add_hooks) +void USBScanner::AddDevice(std::unique_ptr device, DeviceMap* new_devices) const { if (m_host->ShouldAddDevice(*device)) - { - const u64 deviceid = device->GetId(); - new_devices.insert(deviceid); - if (AddDevice(std::move(device)) || always_add_hooks) - { - hooks.emplace(GetDeviceById(deviceid), ChangeEvent::Inserted); - } - } -} - -std::shared_ptr USBScanner::GetDeviceById(const u64 device_id) const -{ - std::lock_guard lk(m_devices_mutex); - const auto it = m_devices.find(device_id); - if (it == m_devices.end()) - return nullptr; - return it->second; + (*new_devices)[device->GetId()] = std::move(device); } } // namespace IOS::HLE diff --git a/Source/Core/Core/IOS/USB/USBScanner.h b/Source/Core/Core/IOS/USB/USBScanner.h index c6594886ee..3d80565926 100644 --- a/Source/Core/Core/IOS/USB/USBScanner.h +++ b/Source/Core/Core/IOS/USB/USBScanner.h @@ -24,33 +24,24 @@ class USBHost; class USBScanner final { public: + using DeviceMap = std::map>; + explicit USBScanner(USBHost* host); ~USBScanner(); void Start(); void Stop(); void WaitForFirstScan(); - bool UpdateDevices(bool always_add_hooks = false); - enum class ChangeEvent - { - Inserted, - Removed, - }; - using DeviceChangeHooks = std::map, ChangeEvent>; + DeviceMap GetDevices() const; private: - bool AddDevice(std::unique_ptr device); - bool AddNewDevices(std::set& new_devices, DeviceChangeHooks& hooks, bool always_add_hooks); - void DetectRemovedDevices(const std::set& plugged_devices, DeviceChangeHooks& hooks); - void AddEmulatedDevices(std::set& new_devices, DeviceChangeHooks& hooks, - bool always_add_hooks); - void CheckAndAddDevice(std::unique_ptr device, std::set& new_devices, - DeviceChangeHooks& hooks, bool always_add_hooks); + bool UpdateDevices(); + bool AddNewDevices(DeviceMap* new_devices) const; + void AddEmulatedDevices(DeviceMap* new_devices) const; + void AddDevice(std::unique_ptr device, DeviceMap* new_devices) const; - std::shared_ptr GetDeviceById(u64 device_id) const; - - std::map> m_devices; + DeviceMap m_devices; mutable std::mutex m_devices_mutex; USBHost* m_host = nullptr;