Files
archived-pcsx2/pcsx2/USB/USB.cpp
GovanifY 132431b7c8 headers: relicense to GPL-3.0+
also update to 2024 while i'm at it
2024-07-30 17:17:13 -04:00

818 lines
21 KiB
C++

// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "Host.h"
#include "StateWrapper.h"
#include "USB/USB.h"
#include "USB/deviceproxy.h"
#include "USB/qemu-usb/USBinternal.h"
#include "USB/qemu-usb/desc.h"
#include "common/Console.h"
#include "common/SettingsInterface.h"
#include "common/WindowInfo.h"
#include "fmt/format.h"
#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <stdexcept>
#include <string>
#define PSXCLK 36864000 /* 36.864 Mhz */
namespace USB
{
OHCIPort& GetOHCIPort(u32 port);
static bool CreateDevice(u32 port);
static void DestroyDevice(u32 port);
static void UpdateDevice(u32 port);
static bool DoOHCIState(StateWrapper& sw);
static void DoEndpointState(USBEndpoint* ep, StateWrapper& sw);
static void DoDeviceState(USBDevice* dev, StateWrapper& sw);
static void DoPacketState(USBPacket* p, StateWrapper& sw, const std::array<bool, 2>& valid_devices);
} // namespace USB
static OHCIState* s_qemu_ohci = nullptr;
static USBDevice* s_usb_device[USB::NUM_PORTS] = {};
static const DeviceProxy* s_usb_device_proxy[USB::NUM_PORTS] = {};
static s64 s_usb_clocks = 0;
static s64 s_usb_remaining = 0;
int64_t g_usb_frame_time = 0;
int64_t g_usb_bit_time = 0;
int64_t g_usb_last_cycle = 0;
std::string USB::GetConfigSection(int port)
{
return fmt::format("USB{}", port + 1);
}
OHCIPort& USB::GetOHCIPort(u32 port)
{
// Apparently the ports on the hub are swapped.
// Get this wrong and games like GT4 won't spin your wheelz.
const u32 rhport = (port == 0) ? 1 : 0;
return s_qemu_ohci->rhport[rhport];
}
bool USB::CreateDevice(u32 port)
{
const Pcsx2Config::USBOptions::Port& portcfg = EmuConfig.USB.Ports[port];
const DeviceProxy* proxy = (portcfg.DeviceType != DEVTYPE_NONE) ? RegisterDevice::instance().Device(portcfg.DeviceType) : nullptr;
if (!proxy)
return true;
DevCon.WriteLn("(USB) Creating a %s in port %u", proxy->Name(), port + 1);
USBDevice* dev;
{
auto lock = Host::GetSettingsLock();
dev = proxy->CreateDevice(*Host::GetSettingsInterface(), port, portcfg.DeviceSubtype);
}
if (!dev)
{
Console.Error("Failed to create USB device in port %u (%s)", port + 1, proxy->Name());
return false;
}
pxAssertRel(s_qemu_ohci, "Has OHCI");
pxAssertRel(!GetOHCIPort(port).port.dev, "No device in OHCI when creating");
GetOHCIPort(port).port.dev = dev;
dev->attached = true;
usb_attach(&GetOHCIPort(port).port);
s_usb_device[port] = dev;
s_usb_device_proxy[port] = proxy;
return true;
}
void USB::DestroyDevice(u32 port)
{
USBDevice* dev = s_usb_device[port];
if (!dev)
return;
if (dev->klass.unrealize)
dev->klass.unrealize(dev);
GetOHCIPort(port).port.dev = nullptr;
s_usb_device[port] = nullptr;
s_usb_device_proxy[port] = nullptr;
}
void USB::UpdateDevice(u32 port)
{
if (!s_usb_device[port])
return;
auto lock = Host::GetSettingsLock();
s_usb_device_proxy[port]->UpdateSettings(s_usb_device[port], *Host::GetSettingsInterface());
}
void USBinit()
{
RegisterDevice::Register();
}
void USBshutdown()
{
RegisterDevice::instance().Unregister();
}
bool USBopen()
{
s_qemu_ohci = ohci_create(0x1f801600, 2);
if (!s_qemu_ohci)
return false;
s_usb_clocks = 0;
s_usb_remaining = 0;
g_usb_last_cycle = 0;
for (u32 port = 0; port < USB::NUM_PORTS; port++)
USB::CreateDevice(port);
return true;
}
void USBclose()
{
for (u32 port = 0; port < USB::NUM_PORTS; port++)
USB::DestroyDevice(port);
free(s_qemu_ohci);
s_qemu_ohci = nullptr;
}
void USBreset()
{
s_usb_clocks = 0;
s_usb_remaining = 0;
g_usb_last_cycle = 0;
ohci_hard_reset(s_qemu_ohci);
}
u8 USBread8(u32 addr)
{
return 0;
}
u16 USBread16(u32 addr)
{
return 0;
}
u32 USBread32(u32 addr)
{
u32 hard;
hard = ohci_mem_read(s_qemu_ohci, addr);
return hard;
}
void USBwrite8(u32 addr, u8 value)
{
}
void USBwrite16(u32 addr, u16 value)
{
}
void USBwrite32(u32 addr, u32 value)
{
ohci_mem_write(s_qemu_ohci, addr, value);
}
bool USB::DoOHCIState(StateWrapper& sw)
{
if (!sw.DoMarker("USBOHCI"))
return false;
sw.Do(&g_usb_last_cycle);
sw.Do(&s_usb_clocks);
sw.Do(&s_usb_remaining);
sw.Do(&s_qemu_ohci->eof_timer);
sw.Do(&s_qemu_ohci->sof_time);
sw.Do(&s_qemu_ohci->ctl);
sw.Do(&s_qemu_ohci->status);
sw.Do(&s_qemu_ohci->intr_status);
sw.Do(&s_qemu_ohci->intr);
sw.Do(&s_qemu_ohci->hcca);
sw.Do(&s_qemu_ohci->ctrl_head);
sw.Do(&s_qemu_ohci->ctrl_cur);
sw.Do(&s_qemu_ohci->bulk_head);
sw.Do(&s_qemu_ohci->bulk_cur);
sw.Do(&s_qemu_ohci->per_cur);
sw.Do(&s_qemu_ohci->done);
sw.Do(&s_qemu_ohci->done_count);
s_qemu_ohci->fsmps = sw.DoBitfield(s_qemu_ohci->fsmps);
s_qemu_ohci->fit = sw.DoBitfield(s_qemu_ohci->fit);
s_qemu_ohci->fi = sw.DoBitfield(s_qemu_ohci->fi);
s_qemu_ohci->frt = sw.DoBitfield(s_qemu_ohci->frt);
sw.Do(&s_qemu_ohci->frame_number);
sw.Do(&s_qemu_ohci->padding);
sw.Do(&s_qemu_ohci->pstart);
sw.Do(&s_qemu_ohci->lst);
sw.Do(&s_qemu_ohci->rhdesc_a);
sw.Do(&s_qemu_ohci->rhdesc_b);
for (u32 i = 0; i < OHCI_MAX_PORTS; i++)
sw.Do(&s_qemu_ohci->rhport[i].ctrl);
sw.Do(&s_qemu_ohci->old_ctl);
sw.DoArray(s_qemu_ohci->usb_buf, sizeof(s_qemu_ohci->usb_buf));
sw.Do(&s_qemu_ohci->async_td);
sw.Do(&s_qemu_ohci->async_complete);
return true;
}
void USB::DoDeviceState(USBDevice* dev, StateWrapper& sw)
{
if (!sw.DoMarker("USBDevice"))
return;
sw.Do(&dev->speed);
sw.Do(&dev->addr);
sw.Do(&dev->state);
sw.DoBytes(&dev->setup_buf, sizeof(dev->setup_buf));
sw.DoBytes(&dev->data_buf, sizeof(dev->data_buf));
sw.Do(&dev->remote_wakeup);
sw.Do(&dev->setup_state);
sw.Do(&dev->setup_len);
sw.Do(&dev->setup_index);
sw.Do(&dev->configuration);
if (sw.IsReading())
usb_desc_set_config(dev, dev->configuration);
int altsetting[USB_MAX_INTERFACES];
std::memcpy(altsetting, dev->altsetting, sizeof(altsetting));
sw.DoPODArray(altsetting, std::size(altsetting));
if (sw.IsReading())
{
for (u32 i = 0; i < USB_MAX_INTERFACES; i++)
{
dev->altsetting[i] = altsetting[i];
usb_desc_set_interface(dev, i, altsetting[i]);
}
}
DoEndpointState(&dev->ep_ctl, sw);
for (u32 i = 0; i < USB_MAX_ENDPOINTS; i++)
DoEndpointState(&dev->ep_in[i], sw);
for (u32 i = 0; i < USB_MAX_ENDPOINTS; i++)
DoEndpointState(&dev->ep_out[i], sw);
}
void USB::DoEndpointState(USBEndpoint* ep, StateWrapper& sw)
{
// assumed the fields above are constant
sw.Do(&ep->pipeline);
sw.Do(&ep->halted);
if (sw.IsReading())
{
// clear out all packets, we'll fill it in later
while (!QTAILQ_EMPTY(&ep->queue))
QTAILQ_REMOVE(&ep->queue, QTAILQ_FIRST(&ep->queue), queue);
}
}
void USB::DoPacketState(USBPacket* p, StateWrapper& sw, const std::array<bool, 2>& valid_devices)
{
if (!sw.DoMarker("USBPacket"))
return;
s32 dev_index = -1;
s32 ep_index = -1;
bool queued = false;
if (sw.IsWriting())
{
USBEndpoint* ep = p->ep;
if (ep)
{
for (u32 i = 0; i < NUM_PORTS; i++)
{
USBDevice* dev = s_usb_device[i];
if (valid_devices[i] && ep->dev == dev)
{
dev_index = static_cast<s32>(i);
if (ep == &dev->ep_ctl)
ep_index = 0;
else if (ep >= &dev->ep_in[0] && ep <= &dev->ep_in[USB_MAX_ENDPOINTS - 1])
ep_index = static_cast<s32>(ep - &dev->ep_in[0]) + 1;
else if (ep >= &dev->ep_out[0] && ep <= &dev->ep_out[USB_MAX_ENDPOINTS - 1])
ep_index = static_cast<s32>(ep - &dev->ep_out[0]) + 1 + USB_MAX_ENDPOINTS;
USBPacket* pp;
QTAILQ_FOREACH(pp, &ep->queue, queue)
{
if (pp == p)
queued = true;
}
break;
}
}
if (dev_index < 0 || ep_index < 0)
Console.Error("Failed to save USB packet from unknown endpoint");
}
}
sw.Do(&dev_index);
sw.Do(&ep_index);
sw.Do(&p->buffer_size);
sw.Do(&queued);
sw.Do(&p->pid);
sw.Do(&p->id);
sw.Do(&p->stream);
sw.Do(&p->parameter);
sw.Do(&p->short_not_ok);
sw.Do(&p->int_req);
sw.Do(&p->status);
sw.Do(&p->actual_length);
sw.Do(&p->state);
if (sw.IsReading())
{
p->ep = nullptr;
if (dev_index >= 0 && ep_index >= 0 && valid_devices[static_cast<u32>(dev_index)])
{
USBDevice* dev = s_usb_device[static_cast<u32>(dev_index)];
pxAssert(dev);
p->buffer_ptr = (p->buffer_size > 0) ? s_qemu_ohci->usb_buf : nullptr;
if (ep_index == 0)
p->ep = &dev->ep_ctl;
else if (ep_index < (1 + USB_MAX_ENDPOINTS))
p->ep = &dev->ep_in[ep_index - 1];
else if (ep_index < (1 + USB_MAX_ENDPOINTS + USB_MAX_ENDPOINTS))
p->ep = &dev->ep_out[ep_index - 1 - USB_MAX_ENDPOINTS];
if (p->ep && queued)
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
}
else
{
p->buffer_ptr = nullptr;
p->buffer_size = 0;
}
}
}
bool USB::DoState(StateWrapper& sw)
{
std::array<bool, 2> valid_devices = {};
if (sw.IsReading())
{
if (!sw.DoMarker("USB") || !USB::DoOHCIState(sw))
{
Console.Error("USB state is invalid, resetting.");
USBreset();
return true;
}
for (u32 port = 0; port < USB::NUM_PORTS; port++)
{
s32 state_devtype;
u32 state_devsubtype;
u32 state_size;
sw.Do(&state_devtype);
sw.Do(&state_devsubtype);
sw.Do(&state_size);
// this is *assuming* the config is correct... there's no reason it shouldn't be.
if (sw.HasError() ||
EmuConfig.USB.Ports[port].DeviceType != state_devtype ||
EmuConfig.USB.Ports[port].DeviceSubtype != state_devsubtype ||
(state_devtype != DEVTYPE_NONE && !s_usb_device[port]))
{
Console.Error("Save state has device type %u, but config has %u. Reattaching device.", state_devtype, EmuConfig.USB.Ports[port].DeviceType);
if (s_usb_device[port])
usb_reattach(&USB::GetOHCIPort(port).port);
sw.SkipBytes(state_size);
continue;
}
if (!s_usb_device[port])
{
// nothing in this port
sw.SkipBytes(state_size);
continue;
}
USB::DoDeviceState(s_usb_device[port], sw);
if (!s_usb_device_proxy[port]->Freeze(s_usb_device[port], sw) || sw.HasError())
{
Console.Error("Failed to deserialize USB port %u, removing device.", port);
USB::DestroyDevice(port);
continue;
}
valid_devices[port] = true;
}
USB::DoPacketState(&s_qemu_ohci->usb_packet, sw, valid_devices);
if (sw.HasError())
{
Console.WriteLn("Failed to read USB packet, resetting all devices.");
USBreset();
return true;
}
}
else
{
if (!sw.DoMarker("USB") || !USB::DoOHCIState(sw))
return false;
for (u32 port = 0; port < USB::NUM_PORTS; port++)
{
s32 state_devtype = EmuConfig.USB.Ports[port].DeviceType;
u32 state_devsubtype = EmuConfig.USB.Ports[port].DeviceSubtype;
sw.Do(&state_devtype);
sw.Do(&state_devsubtype);
const u32 size_pos = sw.GetStream()->GetPosition();
u32 state_size = 0;
sw.Do(&state_size);
if (sw.HasError())
return false;
if (!s_usb_device[port])
{
// nothing in this port
continue;
}
const u32 start_pos = sw.GetStream()->GetPosition();
USB::DoDeviceState(s_usb_device[port], sw);
if (!s_usb_device_proxy[port]->Freeze(s_usb_device[port], sw) || sw.HasError())
{
Console.Error("Failed to serialize USB port %u.", port);
return false;
}
const u32 end_pos = sw.GetStream()->GetPosition();
state_size = end_pos - start_pos;
if (!sw.GetStream()->SeekAbsolute(size_pos) || (sw.Do(&state_size), sw.HasError()) || !sw.GetStream()->SeekAbsolute(end_pos))
return false;
valid_devices[port] = true;
}
USB::DoPacketState(&s_qemu_ohci->usb_packet, sw, valid_devices);
if (sw.HasError())
return false;
}
return true;
}
void USBasync(u32 cycles)
{
if (!s_usb_device[0] && !s_usb_device[1])
return;
s_usb_remaining += cycles;
s_usb_clocks += s_usb_remaining;
if (s_qemu_ohci->eof_timer > 0)
{
while ((uint64_t)s_usb_remaining >= s_qemu_ohci->eof_timer)
{
s_usb_remaining -= s_qemu_ohci->eof_timer;
s_qemu_ohci->eof_timer = 0;
ohci_frame_boundary(s_qemu_ohci);
/*
* Break out of the loop if bus was stopped.
* If ohci_frame_boundary hits an UE, but doesn't stop processing,
* it seems to cause a hang inside the game instead.
*/
if (!s_qemu_ohci->eof_timer)
break;
}
if ((s_usb_remaining > 0) && (s_qemu_ohci->eof_timer > 0))
{
s64 m = s_qemu_ohci->eof_timer;
if (s_usb_remaining < m)
m = s_usb_remaining;
s_qemu_ohci->eof_timer -= m;
s_usb_remaining -= m;
}
}
//if(qemu_ohci->eof_timer <= 0)
//{
//ohci_frame_boundary(qemu_ohci);
//}
}
int usb_get_ticks_per_second()
{
return PSXCLK;
}
s64 usb_get_clock()
{
return s_usb_clocks;
}
s32 USB::DeviceTypeNameToIndex(const std::string_view device)
{
RegisterDevice& rd = RegisterDevice::instance();
return rd.Index(device);
}
const char* USB::DeviceTypeIndexToName(s32 device)
{
RegisterDevice& rd = RegisterDevice::instance();
const DeviceProxy* proxy = (device != DEVTYPE_NONE) ? rd.Device(device) : nullptr;
return proxy ? proxy->TypeName() : TRANSLATE_NOOP("USB", "None");
}
std::vector<std::pair<const char*, const char*>> USB::GetDeviceTypes()
{
RegisterDevice& rd = RegisterDevice::instance();
std::vector<std::pair<const char*, const char*>> ret;
ret.reserve(rd.Map().size() + 1);
ret.emplace_back("None", TRANSLATE_NOOP("USB", "Not Connected"));
for (const auto& it : rd.Map())
ret.emplace_back(it.second->TypeName(), it.second->Name());
return ret;
}
const char* USB::GetDeviceName(const std::string_view device)
{
const DeviceProxy* dev = RegisterDevice::instance().Device(device);
return dev ? dev->Name() : TRANSLATE_NOOP("USB", "Not Connected");
}
const char* USB::GetDeviceSubtypeName(const std::string_view device, u32 subtype)
{
const DeviceProxy* dev = RegisterDevice::instance().Device(device);
if (!dev)
return "Unknown";
const std::span<const char*> subtypes(dev->SubTypes());
if (subtypes.empty() || subtype >= subtypes.size())
return "";
return subtypes[subtype];
}
std::span<const char*> USB::GetDeviceSubtypes(const std::string_view device)
{
const DeviceProxy* dev = RegisterDevice::instance().Device(device);
return dev ? dev->SubTypes() : std::span<const char*>();
}
std::span<const InputBindingInfo> USB::GetDeviceBindings(const std::string_view device, u32 subtype)
{
const DeviceProxy* dev = RegisterDevice::instance().Device(device);
return dev ? dev->Bindings(subtype) : std::span<const InputBindingInfo>();
}
std::span<const SettingInfo> USB::GetDeviceSettings(const std::string_view device, u32 subtype)
{
const DeviceProxy* dev = RegisterDevice::instance().Device(device);
return dev ? dev->Settings(subtype) : std::span<const SettingInfo>();
}
std::span<const InputBindingInfo> USB::GetDeviceBindings(u32 port)
{
pxAssert(port < NUM_PORTS);
if (s_usb_device_proxy[port])
return s_usb_device_proxy[port]->Bindings(EmuConfig.USB.Ports[port].DeviceSubtype);
else
return {};
}
float USB::GetDeviceBindValue(u32 port, u32 bind_index)
{
pxAssert(port < NUM_PORTS);
if (!s_usb_device[port])
return 0.0f;
return s_usb_device_proxy[port]->GetBindingValue(s_usb_device[port], bind_index);
}
void USB::SetDeviceBindValue(u32 port, u32 bind_index, float value)
{
pxAssert(port < NUM_PORTS);
if (!s_usb_device[port])
return;
s_usb_device_proxy[port]->SetBindingValue(s_usb_device[port], bind_index, value);
}
void USB::InputDeviceConnected(const std::string_view identifier)
{
for (u32 i = 0; i < NUM_PORTS; i++)
{
if (s_usb_device[i])
s_usb_device_proxy[i]->InputDeviceConnected(s_usb_device[i], identifier);
}
}
void USB::InputDeviceDisconnected(const std::string_view identifier)
{
for (u32 i = 0; i < NUM_PORTS; i++)
{
if (s_usb_device[i])
s_usb_device_proxy[i]->InputDeviceDisconnected(s_usb_device[i], identifier);
}
}
std::string USB::GetConfigDevice(const SettingsInterface& si, u32 port)
{
return si.GetStringValue(GetConfigSection(port).c_str(), "Type", "None");
}
void USB::SetConfigDevice(SettingsInterface& si, u32 port, const char* devname)
{
si.SetStringValue(GetConfigSection(port).c_str(), "Type", devname);
}
u32 USB::GetConfigSubType(const SettingsInterface& si, u32 port, const std::string_view devname)
{
return si.GetUIntValue(GetConfigSection(port).c_str(), fmt::format("{}_subtype", devname).c_str(), 0u);
}
void USB::SetConfigSubType(SettingsInterface& si, u32 port, const std::string_view devname, u32 subtype)
{
si.SetUIntValue(GetConfigSection(port).c_str(), fmt::format("{}_subtype", devname).c_str(), subtype);
}
std::string USB::GetConfigSubKey(const std::string_view device, const std::string_view bind_name)
{
return fmt::format("{}_{}", device, bind_name);
}
bool USB::ConfigKeyExists(SettingsInterface& si, u32 port, const char* devname, const char* key)
{
const std::string real_key(fmt::format("{}_{}", devname, key));
return si.ContainsValue(GetConfigSection(port).c_str(), real_key.c_str());
}
bool USB::GetConfigBool(SettingsInterface& si, u32 port, const char* devname, const char* key, bool default_value)
{
const std::string real_key(fmt::format("{}_{}", devname, key));
return si.GetBoolValue(GetConfigSection(port).c_str(), real_key.c_str(), default_value);
}
s32 USB::GetConfigInt(SettingsInterface& si, u32 port, const char* devname, const char* key, s32 default_value)
{
const std::string real_key(fmt::format("{}_{}", devname, key));
return si.GetIntValue(GetConfigSection(port).c_str(), real_key.c_str(), default_value);
}
float USB::GetConfigFloat(SettingsInterface& si, u32 port, const char* devname, const char* key, float default_value)
{
const std::string real_key(fmt::format("{}_{}", devname, key));
return si.GetFloatValue(GetConfigSection(port).c_str(), real_key.c_str(), default_value);
}
std::string USB::GetConfigString(SettingsInterface& si, u32 port, const char* devname, const char* key, const char* default_value /*= ""*/)
{
const std::string real_key(fmt::format("{}_{}", devname, key));
return si.GetStringValue(GetConfigSection(port).c_str(), real_key.c_str(), default_value);
}
static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& section, const std::string& type,
const std::vector<std::pair<GenericInputBinding, std::string>>& mapping, GenericInputBinding generic_name,
const char* bind_name)
{
// find the mapping it corresponds to
const std::string* found_mapping = nullptr;
for (const std::pair<GenericInputBinding, std::string>& it : mapping)
{
if (it.first == generic_name)
{
found_mapping = &it.second;
break;
}
}
const std::string key(USB::GetConfigSubKey(type, bind_name));
if (found_mapping)
{
Console.WriteLn("(MapDevice) Map %s/%s to '%s'", section.c_str(), bind_name, found_mapping->c_str());
si.SetStringValue(section.c_str(), key.c_str(), found_mapping->c_str());
return 1;
}
else
{
si.DeleteValue(section.c_str(), key.c_str());
return 0;
}
}
bool USB::MapDevice(SettingsInterface& si, u32 port, const std::vector<std::pair<GenericInputBinding, std::string>>& mapping)
{
const std::string section(GetConfigSection(port));
const std::string type(GetConfigDevice(si, port));
const u32 subtype = GetConfigSubType(si, port, type);
const DeviceProxy* dev = RegisterDevice::instance().Device(type);
if (!dev)
return false;
u32 num_mappings = 0;
for (const InputBindingInfo& bi : dev->Bindings(subtype))
{
if (bi.generic_mapping == GenericInputBinding::Unknown)
continue;
num_mappings += TryMapGenericMapping(si, section, type, mapping, bi.generic_mapping, bi.name);
}
return (num_mappings > 0);
}
void USB::ClearPortBindings(SettingsInterface& si, u32 port)
{
const std::string section = GetConfigSection(port);
const std::string type = GetConfigDevice(si, port);
const u32 subtype = GetConfigSubType(si, port, type);
const DeviceProxy* dev = RegisterDevice::instance().Device(type);
if (!dev)
return;
for (const InputBindingInfo& bi : dev->Bindings(subtype))
si.DeleteValue(section.c_str(), GetConfigSubKey(type, bi.name).c_str());
}
void USB::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si,
bool copy_devices, bool copy_bindings)
{
for (u32 port = 0; port < NUM_PORTS; port++)
{
const std::string section = GetConfigSection(port);
const std::string type = GetConfigDevice(src_si, port);
const u32 subtype = GetConfigSubType(src_si, port, type);
const DeviceProxy* dev = RegisterDevice::instance().Device(type);
if (copy_devices)
{
dest_si->CopyStringValue(src_si, section.c_str(), "Type");
if (dev)
{
dest_si->CopyUIntValue(src_si, section.c_str(), fmt::format("{}_subtype", type).c_str());
for (const SettingInfo& si : dev->Settings(subtype))
si.CopyValue(dest_si, src_si, section.c_str(), GetConfigSubKey(type, si.name).c_str());
}
}
if (copy_bindings && dev)
{
for (const InputBindingInfo& bi : dev->Bindings(subtype))
dest_si->CopyStringValue(src_si, section.c_str(), GetConfigSubKey(type, bi.name).c_str());
}
}
}
void USB::SetDefaultConfiguration(SettingsInterface* si)
{
for (u32 port = 0; port < NUM_PORTS; port++)
{
const std::string section = GetConfigSection(port);
si->ClearSection(section.c_str());
si->SetStringValue(section.c_str(), "Type", "None");
}
}
void USB::CheckForConfigChanges(const Pcsx2Config& old_config)
{
static_assert(Pcsx2Config::USBOptions::NUM_PORTS == NUM_PORTS);
for (u32 port = 0; port < NUM_PORTS; port++)
{
if (EmuConfig.USB.Ports[port] == old_config.USB.Ports[port])
{
UpdateDevice(port);
continue;
}
if (s_usb_device[port])
DestroyDevice(port);
CreateDevice(port);
}
}