IOS: Move USB scanning thread to new class USBScanner

This commit is contained in:
JosJuice 2025-04-06 11:53:25 +02:00
parent c0c180bdc2
commit 427e9c5ad2
14 changed files with 297 additions and 261 deletions

View file

@ -449,6 +449,8 @@ add_library(core
IOS/USB/USB_KBD.h IOS/USB/USB_KBD.h
IOS/USB/USB_VEN/VEN.cpp IOS/USB/USB_VEN/VEN.cpp
IOS/USB/USB_VEN/VEN.h IOS/USB/USB_VEN/VEN.h
IOS/USB/USBScanner.cpp
IOS/USB/USBScanner.h
IOS/USB/USBV0.cpp IOS/USB/USBV0.cpp
IOS/USB/USBV0.h IOS/USB/USBV0.h
IOS/USB/USBV4.cpp IOS/USB/USBV4.cpp

View file

@ -3,30 +3,17 @@
#include "Core/IOS/USB/Host.h" #include "Core/IOS/USB/Host.h"
#include <algorithm> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <optional>
#include <set>
#include <string> #include <string>
#include <utility>
#ifdef __LIBUSB__
#include <libusb.h>
#endif
#include "Common/Assert.h"
#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"
#include "Common/Thread.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/IOS/USB/Common.h" #include "Core/IOS/USB/Common.h"
#include "Core/IOS/USB/Emulated/Infinity.h" #include "Core/IOS/USB/USBScanner.h"
#include "Core/IOS/USB/Emulated/Skylanders/Skylander.h"
#include "Core/IOS/USB/LibusbDevice.h"
#include "Core/NetPlayProto.h"
#include "Core/System.h"
namespace IOS::HLE namespace IOS::HLE
{ {
@ -35,16 +22,19 @@ USBHost::USBHost(EmulationKernel& ios, const std::string& device_name)
{ {
} }
USBHost::~USBHost() = default; USBHost::~USBHost()
{
m_usb_scanner.Stop();
}
std::optional<IPCReply> USBHost::Open(const OpenRequest& request) std::optional<IPCReply> USBHost::Open(const OpenRequest& request)
{ {
if (!m_has_initialised) if (!m_has_initialised)
{ {
GetScanThread().Start(); m_usb_scanner.Start();
// Force a device scan to complete, because some games (including Your Shape) only care // Force a device scan to complete, because some games (including Your Shape) only care
// about the initial device list (in the first GETDEVICECHANGE reply). // about the initial device list (in the first GETDEVICECHANGE reply).
GetScanThread().WaitForFirstScan(); m_usb_scanner.WaitForFirstScan();
m_has_initialised = true; m_has_initialised = true;
} }
return IPCReply(IPC_SUCCESS); return IPCReply(IPC_SUCCESS);
@ -53,9 +43,9 @@ std::optional<IPCReply> USBHost::Open(const OpenRequest& request)
void USBHost::UpdateWantDeterminism(const bool new_want_determinism) void USBHost::UpdateWantDeterminism(const bool new_want_determinism)
{ {
if (new_want_determinism) if (new_want_determinism)
GetScanThread().Stop(); m_usb_scanner.Stop();
else if (IsOpened()) else if (IsOpened())
GetScanThread().Start(); m_usb_scanner.Start();
} }
void USBHost::DoState(PointerWrap& p) void USBHost::DoState(PointerWrap& p)
@ -65,27 +55,13 @@ void USBHost::DoState(PointerWrap& p)
{ {
// After a state has loaded, there may be insertion hooks for devices that were // After a state has loaded, there may be insertion hooks for devices that were
// already plugged in, and which need to be triggered. // already plugged in, and which need to be triggered.
UpdateDevices(true); m_usb_scanner.UpdateDevices(true);
} }
} }
bool USBHost::AddDevice(std::unique_ptr<USB::Device> device)
{
std::lock_guard lk(m_devices_mutex);
if (m_devices.contains(device->GetId()))
return false;
m_devices[device->GetId()] = std::move(device);
return true;
}
std::shared_ptr<USB::Device> USBHost::GetDeviceById(const u64 device_id) const std::shared_ptr<USB::Device> USBHost::GetDeviceById(const u64 device_id) const
{ {
std::lock_guard lk(m_devices_mutex); return m_usb_scanner.GetDeviceById(device_id);
const auto it = m_devices.find(device_id);
if (it == m_devices.end())
return nullptr;
return it->second;
} }
void USBHost::OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> changed_device) void USBHost::OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> changed_device)
@ -104,69 +80,7 @@ bool USBHost::ShouldAddDevice(const USB::Device& device) const
void USBHost::Update() void USBHost::Update()
{ {
if (Core::WantsDeterminism()) if (Core::WantsDeterminism())
UpdateDevices(); m_usb_scanner.UpdateDevices();
}
// This is called from the scan thread. Returns false if we failed to update the device list.
bool USBHost::UpdateDevices(const bool always_add_hooks)
{
DeviceChangeHooks hooks;
std::set<u64> 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))
return false;
DetectRemovedDevices(plugged_devices, hooks);
DispatchHooks(hooks);
return true;
}
bool USBHost::AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
const bool always_add_hooks)
{
AddEmulatedDevices(new_devices, hooks, always_add_hooks);
#ifdef __LIBUSB__
if (!Core::WantsDeterminism())
{
auto whitelist = Config::GetUSBDeviceWhitelist();
if (whitelist.empty())
return true;
if (m_context.IsValid())
{
const int ret = m_context.GetDeviceList([&](libusb_device* device) {
libusb_device_descriptor descriptor;
libusb_get_device_descriptor(device, &descriptor);
if (!whitelist.contains({descriptor.idVendor, descriptor.idProduct}))
return true;
auto usb_device =
std::make_unique<USB::LibusbDevice>(GetEmulationKernel(), device, descriptor);
CheckAndAddDevice(std::move(usb_device), new_devices, hooks, always_add_hooks);
return true;
});
if (ret != LIBUSB_SUCCESS)
WARN_LOG_FMT(IOS_USB, "GetDeviceList failed: {}", LibusbUtils::ErrorWrap(ret));
}
}
#endif
return true;
}
void USBHost::DetectRemovedDevices(const std::set<u64>& 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 USBHost::DispatchHooks(const DeviceChangeHooks& hooks) void USBHost::DispatchHooks(const DeviceChangeHooks& hooks)
@ -182,80 +96,6 @@ void USBHost::DispatchHooks(const DeviceChangeHooks& hooks)
OnDeviceChangeEnd(); OnDeviceChangeEnd();
} }
void USBHost::AddEmulatedDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
bool always_add_hooks)
{
if (Config::Get(Config::MAIN_EMULATE_SKYLANDER_PORTAL) && !NetPlay::IsNetPlayRunning())
{
auto skylanderportal = std::make_unique<USB::SkylanderUSB>(GetEmulationKernel());
CheckAndAddDevice(std::move(skylanderportal), new_devices, hooks, always_add_hooks);
}
if (Config::Get(Config::MAIN_EMULATE_INFINITY_BASE) && !NetPlay::IsNetPlayRunning())
{
auto infinity_base = std::make_unique<USB::InfinityUSB>(GetEmulationKernel());
CheckAndAddDevice(std::move(infinity_base), new_devices, hooks, always_add_hooks);
}
}
void USBHost::CheckAndAddDevice(std::unique_ptr<USB::Device> device, std::set<u64>& new_devices,
DeviceChangeHooks& hooks, bool always_add_hooks)
{
if (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);
}
}
}
USBHost::ScanThread::~ScanThread()
{
Stop();
}
void USBHost::ScanThread::WaitForFirstScan()
{
if (m_thread_running.IsSet())
{
m_first_scan_complete_event.Wait();
}
}
void USBHost::ScanThread::Start()
{
if (Core::WantsDeterminism())
{
m_host->UpdateDevices();
return;
}
if (m_thread_running.TestAndSet())
{
m_thread = std::thread([this] {
Common::SetCurrentThreadName("USB Scan Thread");
while (m_thread_running.IsSet())
{
if (m_host->UpdateDevices())
m_first_scan_complete_event.Set();
Common::SleepCurrentThread(50);
}
});
}
}
void USBHost::ScanThread::Stop()
{
if (m_thread_running.TestAndClear())
m_thread.join();
// Clear all devices and dispatch removal hooks.
DeviceChangeHooks hooks;
m_host->DetectRemovedDevices(std::set<u64>(), hooks);
m_host->DispatchHooks(hooks);
}
std::optional<IPCReply> USBHost::HandleTransfer(std::shared_ptr<USB::Device> device, u32 request, std::optional<IPCReply> USBHost::HandleTransfer(std::shared_ptr<USB::Device> device, u32 request,
std::function<s32()> submit) const std::function<s32()> submit) const
{ {
@ -270,4 +110,5 @@ std::optional<IPCReply> USBHost::HandleTransfer(std::shared_ptr<USB::Device> dev
device->GetVid(), device->GetPid(), request, device->GetErrorName(ret)); device->GetVid(), device->GetPid(), request, device->GetErrorName(ret));
return IPCReply(ret <= 0 ? ret : IPC_EINVAL); return IPCReply(ret <= 0 ? ret : IPC_EINVAL);
} }
} // namespace IOS::HLE } // namespace IOS::HLE

View file

@ -3,23 +3,16 @@
#pragma once #pragma once
#include <cstddef>
#include <functional> #include <functional>
#include <map>
#include <memory> #include <memory>
#include <mutex> #include <optional>
#include <set>
#include <string> #include <string>
#include <thread>
#include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Core/IOS/Device.h" #include "Core/IOS/Device.h"
#include "Core/IOS/IOS.h" #include "Core/IOS/IOS.h"
#include "Core/IOS/USB/Common.h" #include "Core/IOS/USB/Common.h"
#include "Core/LibusbUtils.h" #include "Core/IOS/USB/USBScanner.h"
class PointerWrap; class PointerWrap;
@ -37,56 +30,26 @@ public:
void UpdateWantDeterminism(bool new_want_determinism) override; void UpdateWantDeterminism(bool new_want_determinism) override;
void DoState(PointerWrap& p) override; void DoState(PointerWrap& p) override;
virtual bool ShouldAddDevice(const USB::Device& device) const;
void DispatchHooks(const USBScanner::DeviceChangeHooks& hooks);
protected: protected:
enum class ChangeEvent using ChangeEvent = USBScanner::ChangeEvent;
{ using DeviceChangeHooks = USBScanner::DeviceChangeHooks;
Inserted,
Removed,
};
using DeviceChangeHooks = std::map<std::shared_ptr<USB::Device>, ChangeEvent>;
class ScanThread final
{
public:
explicit ScanThread(USBHost* host) : m_host(host) {}
~ScanThread();
void Start();
void Stop();
void WaitForFirstScan();
private:
USBHost* m_host = nullptr;
Common::Flag m_thread_running;
std::thread m_thread;
Common::Event m_first_scan_complete_event;
Common::Flag m_is_initialized;
};
std::map<u64, std::shared_ptr<USB::Device>> m_devices;
mutable std::mutex m_devices_mutex;
std::shared_ptr<USB::Device> GetDeviceById(u64 device_id) const; std::shared_ptr<USB::Device> GetDeviceById(u64 device_id) const;
virtual void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> changed_device); virtual void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> changed_device);
virtual void OnDeviceChangeEnd(); virtual void OnDeviceChangeEnd();
virtual bool ShouldAddDevice(const USB::Device& device) const;
virtual ScanThread& GetScanThread() = 0;
std::optional<IPCReply> HandleTransfer(std::shared_ptr<USB::Device> device, u32 request, std::optional<IPCReply> HandleTransfer(std::shared_ptr<USB::Device> device, u32 request,
std::function<s32()> submit) const; std::function<s32()> submit) const;
USBScanner m_usb_scanner{this};
private: private:
bool AddDevice(std::unique_ptr<USB::Device> device);
void Update() override; void Update() override;
bool UpdateDevices(bool always_add_hooks = false);
bool AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks, bool always_add_hooks);
void DetectRemovedDevices(const std::set<u64>& plugged_devices, DeviceChangeHooks& hooks);
void DispatchHooks(const DeviceChangeHooks& hooks);
void AddEmulatedDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
bool always_add_hooks);
void CheckAndAddDevice(std::unique_ptr<USB::Device> device, std::set<u64>& new_devices,
DeviceChangeHooks& hooks, bool always_add_hooks);
bool m_has_initialised = false; bool m_has_initialised = false;
LibusbUtils::Context m_context;
}; };
} // namespace IOS::HLE } // namespace IOS::HLE

View file

@ -26,10 +26,7 @@ OH0::OH0(EmulationKernel& ios, const std::string& device_name) : USBHost(ios, de
{ {
} }
OH0::~OH0() OH0::~OH0() = default;
{
m_scan_thread.Stop();
}
std::optional<IPCReply> OH0::Open(const OpenRequest& request) std::optional<IPCReply> OH0::Open(const OpenRequest& request)
{ {
@ -76,7 +73,7 @@ std::optional<IPCReply> OH0::IOCtlV(const IOCtlVRequest& request)
void OH0::DoState(PointerWrap& p) void OH0::DoState(PointerWrap& p)
{ {
if (p.IsReadMode() && !m_devices.empty()) if (p.IsReadMode() && !m_usb_scanner.m_devices.empty())
{ {
Core::DisplayMessage("It is suggested that you unplug and replug all connected USB devices.", Core::DisplayMessage("It is suggested that you unplug and replug all connected USB devices.",
5000); 5000);
@ -117,8 +114,8 @@ IPCReply OH0::GetDeviceList(const IOCtlVRequest& request) const
const u8 interface_class = memory.Read_U8(request.in_vectors[1].address); const u8 interface_class = memory.Read_U8(request.in_vectors[1].address);
u8 entries_count = 0; u8 entries_count = 0;
std::lock_guard lk(m_devices_mutex); std::lock_guard lk(m_usb_scanner.m_devices_mutex);
for (const auto& device : m_devices) for (const auto& device : m_usb_scanner.m_devices)
{ {
if (entries_count >= max_entries_count) if (entries_count >= max_entries_count)
break; break;
@ -234,14 +231,14 @@ std::optional<IPCReply> OH0::RegisterClassChangeHook(const IOCtlVRequest& reques
bool OH0::HasDeviceWithVidPid(const u16 vid, const u16 pid) const bool OH0::HasDeviceWithVidPid(const u16 vid, const u16 pid) const
{ {
return std::ranges::any_of(m_devices, [=](const auto& device) { return std::ranges::any_of(m_usb_scanner.m_devices, [=](const auto& device) {
return device.second->GetVid() == vid && device.second->GetPid() == pid; return device.second->GetVid() == vid && device.second->GetPid() == pid;
}); });
} }
void OH0::OnDeviceChange(const ChangeEvent event, std::shared_ptr<USB::Device> device) void OH0::OnDeviceChange(const ChangeEvent event, std::shared_ptr<USB::Device> device)
{ {
std::lock_guard lk(m_devices_mutex); std::lock_guard lk(m_usb_scanner.m_devices_mutex);
if (event == ChangeEvent::Inserted) if (event == ChangeEvent::Inserted)
TriggerHook(m_insertion_hooks, {device->GetVid(), device->GetPid()}, IPC_SUCCESS); TriggerHook(m_insertion_hooks, {device->GetVid(), device->GetPid()}, IPC_SUCCESS);
else if (event == ChangeEvent::Removed) else if (event == ChangeEvent::Removed)
@ -262,10 +259,10 @@ void OH0::TriggerHook(std::map<T, u32>& hooks, T value, const ReturnCode return_
std::pair<ReturnCode, u64> OH0::DeviceOpen(const u16 vid, const u16 pid) std::pair<ReturnCode, u64> OH0::DeviceOpen(const u16 vid, const u16 pid)
{ {
std::lock_guard lk(m_devices_mutex); std::lock_guard lk(m_usb_scanner.m_devices_mutex);
bool has_device_with_vid_pid = false; bool has_device_with_vid_pid = false;
for (const auto& device : m_devices) for (const auto& device : m_usb_scanner.m_devices)
{ {
if (device.second->GetVid() != vid || device.second->GetPid() != pid) if (device.second->GetVid() != vid || device.second->GetPid() != pid)
continue; continue;

View file

@ -64,8 +64,6 @@ private:
template <typename T> template <typename T>
void TriggerHook(std::map<T, u32>& hooks, T value, ReturnCode return_value); void TriggerHook(std::map<T, u32>& hooks, T value, ReturnCode return_value);
ScanThread& GetScanThread() override { return m_scan_thread; }
struct DeviceEntry struct DeviceEntry
{ {
u32 unknown; u32 unknown;
@ -79,7 +77,5 @@ private:
std::map<u64, u32> m_removal_hooks; std::map<u64, u32> m_removal_hooks;
std::set<u64> m_opened_devices; std::set<u64> m_opened_devices;
std::mutex m_hooks_mutex; std::mutex m_hooks_mutex;
ScanThread m_scan_thread{this};
}; };
} // namespace IOS::HLE } // namespace IOS::HLE

View file

@ -0,0 +1,191 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/IOS/USB/USBScanner.h"
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include <utility>
#ifdef __LIBUSB__
#include <libusb.h>
#endif
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/Thread.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/IOS/USB/Common.h"
#include "Core/IOS/USB/Emulated/Infinity.h"
#include "Core/IOS/USB/Emulated/Skylanders/Skylander.h"
#include "Core/IOS/USB/Host.h"
#include "Core/IOS/USB/LibusbDevice.h"
#include "Core/NetPlayProto.h"
#include "Core/System.h"
namespace IOS::HLE
{
USBScanner::USBScanner(USBHost* host) : m_host(host)
{
}
USBScanner::~USBScanner()
{
Stop();
}
void USBScanner::WaitForFirstScan()
{
if (m_thread_running.IsSet())
{
m_first_scan_complete_event.Wait();
}
}
void USBScanner::Start()
{
if (Core::WantsDeterminism())
{
UpdateDevices();
return;
}
if (m_thread_running.TestAndSet())
{
m_thread = std::thread([this] {
Common::SetCurrentThreadName("USB Scan Thread");
while (m_thread_running.IsSet())
{
if (UpdateDevices())
m_first_scan_complete_event.Set();
Common::SleepCurrentThread(50);
}
});
}
}
void USBScanner::Stop()
{
if (m_thread_running.TestAndClear())
m_thread.join();
// Clear all devices and dispatch removal hooks.
DeviceChangeHooks hooks;
DetectRemovedDevices(std::set<u64>(), hooks);
m_host->DispatchHooks(hooks);
}
// 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)
{
DeviceChangeHooks hooks;
std::set<u64> 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))
return false;
DetectRemovedDevices(plugged_devices, hooks);
m_host->DispatchHooks(hooks);
return true;
}
bool USBScanner::AddDevice(std::unique_ptr<USB::Device> device)
{
std::lock_guard lk(m_devices_mutex);
if (m_devices.contains(device->GetId()))
return false;
m_devices[device->GetId()] = std::move(device);
return true;
}
bool USBScanner::AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
const bool always_add_hooks)
{
AddEmulatedDevices(new_devices, hooks, always_add_hooks);
#ifdef __LIBUSB__
if (!Core::WantsDeterminism())
{
auto whitelist = Config::GetUSBDeviceWhitelist();
if (whitelist.empty())
return true;
if (m_context.IsValid())
{
const int ret = m_context.GetDeviceList([&](libusb_device* device) {
libusb_device_descriptor descriptor;
libusb_get_device_descriptor(device, &descriptor);
if (!whitelist.contains({descriptor.idVendor, descriptor.idProduct}))
return true;
auto usb_device =
std::make_unique<USB::LibusbDevice>(m_host->GetEmulationKernel(), device, descriptor);
CheckAndAddDevice(std::move(usb_device), new_devices, hooks, always_add_hooks);
return true;
});
if (ret != LIBUSB_SUCCESS)
WARN_LOG_FMT(IOS_USB, "GetDeviceList failed: {}", LibusbUtils::ErrorWrap(ret));
}
}
#endif
return true;
}
void USBScanner::DetectRemovedDevices(const std::set<u64>& 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<u64>& new_devices, DeviceChangeHooks& hooks,
bool always_add_hooks)
{
if (Config::Get(Config::MAIN_EMULATE_SKYLANDER_PORTAL) && !NetPlay::IsNetPlayRunning())
{
auto skylanderportal = std::make_unique<USB::SkylanderUSB>(m_host->GetEmulationKernel());
CheckAndAddDevice(std::move(skylanderportal), new_devices, hooks, always_add_hooks);
}
if (Config::Get(Config::MAIN_EMULATE_INFINITY_BASE) && !NetPlay::IsNetPlayRunning())
{
auto infinity_base = std::make_unique<USB::InfinityUSB>(m_host->GetEmulationKernel());
CheckAndAddDevice(std::move(infinity_base), new_devices, hooks, always_add_hooks);
}
}
void USBScanner::CheckAndAddDevice(std::unique_ptr<USB::Device> device, std::set<u64>& new_devices,
DeviceChangeHooks& hooks, bool always_add_hooks)
{
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<USB::Device> 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;
}
} // namespace IOS::HLE

View file

@ -0,0 +1,64 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Core/IOS/USB/Common.h"
#include "Core/LibusbUtils.h"
class PointerWrap;
namespace IOS::HLE
{
class USBHost;
class USBScanner final
{
public:
explicit USBScanner(USBHost* host);
~USBScanner();
void Start();
void Stop();
void WaitForFirstScan();
bool UpdateDevices(bool always_add_hooks = false);
std::shared_ptr<USB::Device> GetDeviceById(u64 device_id) const;
enum class ChangeEvent
{
Inserted,
Removed,
};
using DeviceChangeHooks = std::map<std::shared_ptr<USB::Device>, ChangeEvent>;
std::map<u64, std::shared_ptr<USB::Device>> m_devices;
mutable std::mutex m_devices_mutex;
private:
bool AddDevice(std::unique_ptr<USB::Device> device);
bool AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks, bool always_add_hooks);
void DetectRemovedDevices(const std::set<u64>& plugged_devices, DeviceChangeHooks& hooks);
void AddEmulatedDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks,
bool always_add_hooks);
void CheckAndAddDevice(std::unique_ptr<USB::Device> device, std::set<u64>& new_devices,
DeviceChangeHooks& hooks, bool always_add_hooks);
USBHost* m_host = nullptr;
Common::Flag m_thread_running;
std::thread m_thread;
Common::Event m_first_scan_complete_event;
LibusbUtils::Context m_context;
};
} // namespace IOS::HLE

View file

@ -27,10 +27,7 @@ USB_HIDv4::USB_HIDv4(EmulationKernel& ios, const std::string& device_name)
{ {
} }
USB_HIDv4::~USB_HIDv4() USB_HIDv4::~USB_HIDv4() = default;
{
m_scan_thread.Stop();
}
std::optional<IPCReply> USB_HIDv4::IOCtl(const IOCtlRequest& request) std::optional<IPCReply> USB_HIDv4::IOCtl(const IOCtlRequest& request)
{ {
@ -210,10 +207,10 @@ void USB_HIDv4::TriggerDeviceChangeReply()
auto& memory = system.GetMemory(); auto& memory = system.GetMemory();
{ {
std::lock_guard lk(m_devices_mutex); std::lock_guard lk(m_usb_scanner.m_devices_mutex);
const u32 dest = m_devicechange_hook_request->buffer_out; const u32 dest = m_devicechange_hook_request->buffer_out;
u32 offset = 0; u32 offset = 0;
for (const auto& device : m_devices) for (const auto& device : m_usb_scanner.m_devices)
{ {
const std::vector<u8> device_section = GetDeviceEntry(*device.second.get()); const std::vector<u8> device_section = GetDeviceEntry(*device.second.get());
if (offset + device_section.size() > m_devicechange_hook_request->buffer_out_size - 1) if (offset + device_section.size() > m_devicechange_hook_request->buffer_out_size - 1)

View file

@ -39,7 +39,6 @@ private:
std::vector<u8> GetDeviceEntry(const USB::Device& device) const; std::vector<u8> GetDeviceEntry(const USB::Device& device) const;
void OnDeviceChange(ChangeEvent, std::shared_ptr<USB::Device>) override; void OnDeviceChange(ChangeEvent, std::shared_ptr<USB::Device>) override;
bool ShouldAddDevice(const USB::Device& device) const override; bool ShouldAddDevice(const USB::Device& device) const override;
ScanThread& GetScanThread() override { return m_scan_thread; }
static constexpr u32 VERSION = 0x40001; static constexpr u32 VERSION = 0x40001;
static constexpr u8 HID_CLASS = 0x03; static constexpr u8 HID_CLASS = 0x03;
@ -53,7 +52,5 @@ private:
// IOS device IDs <=> USB device IDs // IOS device IDs <=> USB device IDs
std::map<s32, u64> m_ios_ids; std::map<s32, u64> m_ios_ids;
std::map<u64, s32> m_device_ids; std::map<u64, s32> m_device_ids;
ScanThread m_scan_thread{this};
}; };
} // namespace IOS::HLE } // namespace IOS::HLE

View file

@ -18,10 +18,7 @@ namespace IOS::HLE
{ {
constexpr u32 USBV5_VERSION = 0x50001; constexpr u32 USBV5_VERSION = 0x50001;
USB_HIDv5::~USB_HIDv5() USB_HIDv5::~USB_HIDv5() = default;
{
m_scan_thread.Stop();
}
std::optional<IPCReply> USB_HIDv5::IOCtl(const IOCtlRequest& request) std::optional<IPCReply> USB_HIDv5::IOCtl(const IOCtlRequest& request)
{ {

View file

@ -27,15 +27,11 @@ private:
bool ShouldAddDevice(const USB::Device& device) const override; bool ShouldAddDevice(const USB::Device& device) const override;
bool HasInterfaceNumberInIDs() const override { return true; } bool HasInterfaceNumberInIDs() const override { return true; }
ScanThread& GetScanThread() override { return m_scan_thread; }
struct AdditionalDeviceData struct AdditionalDeviceData
{ {
u8 interrupt_in_endpoint = 0; u8 interrupt_in_endpoint = 0;
u8 interrupt_out_endpoint = 0; u8 interrupt_out_endpoint = 0;
}; };
std::array<AdditionalDeviceData, 32> m_additional_device_data{}; std::array<AdditionalDeviceData, 32> m_additional_device_data{};
ScanThread m_scan_thread{this};
}; };
} // namespace IOS::HLE } // namespace IOS::HLE

View file

@ -18,10 +18,7 @@ namespace IOS::HLE
{ {
constexpr u32 USBV5_VERSION = 0x50001; constexpr u32 USBV5_VERSION = 0x50001;
USB_VEN::~USB_VEN() USB_VEN::~USB_VEN() = default;
{
m_scan_thread.Stop();
}
std::optional<IPCReply> USB_VEN::IOCtl(const IOCtlRequest& request) std::optional<IPCReply> USB_VEN::IOCtl(const IOCtlRequest& request)
{ {

View file

@ -25,9 +25,5 @@ private:
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv); s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv);
bool HasInterfaceNumberInIDs() const override { return false; } bool HasInterfaceNumberInIDs() const override { return false; }
ScanThread& GetScanThread() override { return m_scan_thread; }
ScanThread m_scan_thread{this};
}; };
} // namespace IOS::HLE } // namespace IOS::HLE

View file

@ -413,6 +413,7 @@
<ClInclude Include="Core\IOS\USB\USB_HID\HIDv5.h" /> <ClInclude Include="Core\IOS\USB\USB_HID\HIDv5.h" />
<ClInclude Include="Core\IOS\USB\USB_KBD.h" /> <ClInclude Include="Core\IOS\USB\USB_KBD.h" />
<ClInclude Include="Core\IOS\USB\USB_VEN\VEN.h" /> <ClInclude Include="Core\IOS\USB\USB_VEN\VEN.h" />
<ClInclude Include="Core\IOS\USB\USBScanner.h" />
<ClInclude Include="Core\IOS\USB\USBV0.h" /> <ClInclude Include="Core\IOS\USB\USBV0.h" />
<ClInclude Include="Core\IOS\USB\USBV4.h" /> <ClInclude Include="Core\IOS\USB\USBV4.h" />
<ClInclude Include="Core\IOS\USB\USBV5.h" /> <ClInclude Include="Core\IOS\USB\USBV5.h" />
@ -1077,6 +1078,7 @@
<ClCompile Include="Core\IOS\USB\USB_HID\HIDv5.cpp" /> <ClCompile Include="Core\IOS\USB\USB_HID\HIDv5.cpp" />
<ClCompile Include="Core\IOS\USB\USB_KBD.cpp" /> <ClCompile Include="Core\IOS\USB\USB_KBD.cpp" />
<ClCompile Include="Core\IOS\USB\USB_VEN\VEN.cpp" /> <ClCompile Include="Core\IOS\USB\USB_VEN\VEN.cpp" />
<ClCompile Include="Core\IOS\USB\USBScanner.cpp" />
<ClCompile Include="Core\IOS\USB\USBV0.cpp" /> <ClCompile Include="Core\IOS\USB\USBV0.cpp" />
<ClCompile Include="Core\IOS\USB\USBV4.cpp" /> <ClCompile Include="Core\IOS\USB\USBV4.cpp" />
<ClCompile Include="Core\IOS\USB\USBV5.cpp" /> <ClCompile Include="Core\IOS\USB\USBV5.cpp" />