diff --git a/Externals/libusb/libusb_static_2013.vcxproj b/Externals/libusb/libusb_static_2013.vcxproj
index 2aac3e8db1..74397eb70d 100644
--- a/Externals/libusb/libusb_static_2013.vcxproj
+++ b/Externals/libusb/libusb_static_2013.vcxproj
@@ -52,7 +52,7 @@
-
+
@@ -65,7 +65,7 @@
-
+
diff --git a/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj b/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj
index 67944bafb7..b02ad158f6 100644
--- a/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj
+++ b/Externals/libusb/msvc/libusb_usbdk_static_2015.vcxproj
@@ -159,4 +159,4 @@
-
\ No newline at end of file
+
diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index 2a5ff1d4d8..5f910a1670 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -34,6 +34,11 @@ set(SRCS Analytics.cpp
Crypto/ec.cpp
Logging/LogManager.cpp)
+if(LIBUSB_FOUND)
+ set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
+ set(SRCS ${SRCS} LibusbContext.cpp)
+endif(LIBUSB_FOUND)
+
if(ANDROID)
set(SRCS ${SRCS}
Logging/ConsoleListenerDroid.cpp)
diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj
index 52f408e776..831dcad4a7 100644
--- a/Source/Core/Common/Common.vcxproj
+++ b/Source/Core/Common/Common.vcxproj
@@ -113,6 +113,7 @@
+
@@ -159,6 +160,9 @@
+
+ 4200;%(DisableSpecificWarnings)
+
diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters
index 7731482020..3c9b952742 100644
--- a/Source/Core/Common/Common.vcxproj.filters
+++ b/Source/Core/Common/Common.vcxproj.filters
@@ -45,6 +45,7 @@
+
@@ -236,6 +237,7 @@
+
diff --git a/Source/Core/Common/LibusbContext.cpp b/Source/Core/Common/LibusbContext.cpp
new file mode 100644
index 0000000000..a2ed128200
--- /dev/null
+++ b/Source/Core/Common/LibusbContext.cpp
@@ -0,0 +1,46 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+#include
+#include
+
+#include "Common/LibusbContext.h"
+#include "Common/MsgHandler.h"
+
+namespace LibusbContext
+{
+static std::shared_ptr s_libusb_context;
+static std::once_flag s_context_initialized;
+
+static libusb_context* Create()
+{
+ libusb_context* context;
+ const int ret = libusb_init(&context);
+ if (ret < LIBUSB_SUCCESS)
+ {
+ bool is_windows = false;
+#ifdef _WIN32
+ is_windows = true;
+#endif
+ if (is_windows && ret == LIBUSB_ERROR_NOT_FOUND)
+ PanicAlertT("Failed to initialize libusb because usbdk is not installed.");
+ else
+ PanicAlertT("Failed to initialize libusb: %s", libusb_error_name(ret));
+ return nullptr;
+ }
+ return context;
+}
+
+std::shared_ptr Get()
+{
+ std::call_once(s_context_initialized, []() {
+ s_libusb_context.reset(Create(), [](auto* context) {
+ if (context != nullptr)
+ libusb_exit(context);
+ });
+ });
+ return s_libusb_context;
+}
+}
diff --git a/Source/Core/Common/LibusbContext.h b/Source/Core/Common/LibusbContext.h
new file mode 100644
index 0000000000..99af12ba4c
--- /dev/null
+++ b/Source/Core/Common/LibusbContext.h
@@ -0,0 +1,15 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+
+struct libusb_context;
+
+namespace LibusbContext
+{
+// libusb on Windows is limited to only a single context. Trying to open more
+// than one can cause issues with device enumerations.
+// libusb is thread-safe so this context can be safely used from different threads.
+std::shared_ptr Get();
+}
diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h
index ce46bcb8e2..3ef07acbf1 100644
--- a/Source/Core/Common/Logging/Log.h
+++ b/Source/Core/Common/Logging/Log.h
@@ -31,7 +31,6 @@ enum LOG_TYPE
IOS_DI,
IOS_ES,
IOS_FILEIO,
- IOS_HID,
IOS_NET,
IOS_SD,
IOS_SSL,
diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp
index 4cb786095b..964a8df973 100644
--- a/Source/Core/Common/Logging/LogManager.cpp
+++ b/Source/Core/Common/Logging/LogManager.cpp
@@ -65,7 +65,6 @@ LogManager::LogManager()
m_Log[LogTypes::IOS_DI] = new LogContainer("IOS_DI", "IOS - Drive Interface");
m_Log[LogTypes::IOS_ES] = new LogContainer("IOS_ES", "IOS - ETicket Services");
m_Log[LogTypes::IOS_FILEIO] = new LogContainer("IOS_FILEIO", "IOS - FileIO");
- m_Log[LogTypes::IOS_HID] = new LogContainer("IOS_HID", "IOS - USB_HID");
m_Log[LogTypes::IOS_SD] = new LogContainer("IOS_SD", "IOS - SDIO");
m_Log[LogTypes::IOS_SSL] = new LogContainer("IOS_SSL", "IOS - SSL");
m_Log[LogTypes::IOS_STM] = new LogContainer("IOS_STM", "IOS - State Transition Manager");
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index ed0cecdcc5..166eb158bf 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -154,9 +154,15 @@ set(SRCS ActionReplay.cpp
IOS/SDIO/SDIOSlot0.cpp
IOS/STM/STM.cpp
IOS/USB/Common.cpp
+ IOS/USB/Host.cpp
+ IOS/USB/OH0/OH0.cpp
+ IOS/USB/OH0/OH0Device.cpp
+ IOS/USB/USB_HID/HIDv4.cpp
+ IOS/USB/USB_VEN/VEN.cpp
IOS/USB/USBV0.cpp
+ IOS/USB/USBV4.cpp
+ IOS/USB/USBV5.cpp
IOS/USB/USB_KBD.cpp
- IOS/USB/USB_VEN.cpp
IOS/USB/Bluetooth/BTBase.cpp
IOS/USB/Bluetooth/BTEmu.cpp
IOS/USB/Bluetooth/BTStub.cpp
@@ -264,7 +270,7 @@ set(LIBS
if(LIBUSB_FOUND)
# Using shared LibUSB
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
- set(SRCS ${SRCS} IOS/USB/USB_HIDv4.cpp
+ set(SRCS ${SRCS} IOS/USB/LibusbDevice.cpp
IOS/USB/Bluetooth/BTReal.cpp)
endif()
diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp
index 6240a10272..c5e2b5891a 100644
--- a/Source/Core/Core/ConfigManager.cpp
+++ b/Source/Core/Core/ConfigManager.cpp
@@ -77,6 +77,7 @@ void SConfig::SaveSettings()
SaveAnalyticsSettings(ini);
SaveNetworkSettings(ini);
SaveBluetoothPassthroughSettings(ini);
+ SaveUSBPassthroughSettings(ini);
SaveSysconfSettings(ini);
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
@@ -343,6 +344,20 @@ void SConfig::SaveBluetoothPassthroughSettings(IniFile& ini)
section->Set("LinkKeys", m_bt_passthrough_link_keys);
}
+void SConfig::SaveUSBPassthroughSettings(IniFile& ini)
+{
+ IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
+
+ std::ostringstream oss;
+ for (const auto& device : m_usb_passthrough_devices)
+ oss << StringFromFormat("%04x:%04x", device.first, device.second) << ',';
+ std::string devices_string = oss.str();
+ if (!devices_string.empty())
+ devices_string.pop_back();
+
+ section->Set("Devices", devices_string);
+}
+
void SConfig::SaveSysconfSettings(IniFile& ini)
{
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
@@ -400,6 +415,7 @@ void SConfig::LoadSettings()
LoadNetworkSettings(ini);
LoadAnalyticsSettings(ini);
LoadBluetoothPassthroughSettings(ini);
+ LoadUSBPassthroughSettings(ini);
LoadSysconfSettings(ini);
}
@@ -666,6 +682,27 @@ void SConfig::LoadBluetoothPassthroughSettings(IniFile& ini)
section->Get("LinkKeys", &m_bt_passthrough_link_keys, "");
}
+void SConfig::LoadUSBPassthroughSettings(IniFile& ini)
+{
+ IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
+ m_usb_passthrough_devices.clear();
+ std::string devices_string;
+ std::vector pairs;
+ section->Get("Devices", &devices_string, "");
+ SplitString(devices_string, ',', pairs);
+ for (const auto& pair : pairs)
+ {
+ 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)
+ m_usb_passthrough_devices.emplace(vid, pid);
+ }
+}
+
void SConfig::LoadSysconfSettings(IniFile& ini)
{
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
@@ -757,6 +794,11 @@ void SConfig::LoadDefaults()
m_revision = 0;
}
+bool SConfig::IsUSBDeviceWhitelisted(const std::pair vid_pid) const
+{
+ return m_usb_passthrough_devices.find(vid_pid) != m_usb_passthrough_devices.end();
+}
+
const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
{
switch (region)
diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h
index 8aeb3ed5da..081e63632f 100644
--- a/Source/Core/Core/ConfigManager.h
+++ b/Source/Core/Core/ConfigManager.h
@@ -5,7 +5,9 @@
#pragma once
#include
+#include
#include
+#include
#include
#include "Common/IniFile.h"
@@ -151,6 +153,10 @@ struct SConfig : NonCopyable
int m_bt_passthrough_vid = -1;
std::string m_bt_passthrough_link_keys;
+ // USB passthrough settings
+ std::set> m_usb_passthrough_devices;
+ bool IsUSBDeviceWhitelisted(std::pair vid_pid) const;
+
// SYSCONF settings
int m_sensor_bar_position = 0x01;
int m_sensor_bar_sensitivity = 0x03;
@@ -351,6 +357,7 @@ private:
void SaveNetworkSettings(IniFile& ini);
void SaveAnalyticsSettings(IniFile& ini);
void SaveBluetoothPassthroughSettings(IniFile& ini);
+ void SaveUSBPassthroughSettings(IniFile& ini);
void SaveSysconfSettings(IniFile& ini);
void LoadGeneralSettings(IniFile& ini);
@@ -365,6 +372,7 @@ private:
void LoadNetworkSettings(IniFile& ini);
void LoadAnalyticsSettings(IniFile& ini);
void LoadBluetoothPassthroughSettings(IniFile& ini);
+ void LoadUSBPassthroughSettings(IniFile& ini);
void LoadSysconfSettings(IniFile& ini);
bool SetRegion(DiscIO::Region region, std::string* directory_name);
diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp
index 8abf7ac0c7..f580b6f795 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -54,7 +54,7 @@
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h"
#include "Core/HW/Wiimote.h"
-#include "Core/IOS/Network/Socket.h"
+#include "Core/IOS/IPC.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
#include "Core/Movie.h"
@@ -975,7 +975,7 @@ void UpdateWantDeterminism(bool initial)
bool was_unpaused = Core::PauseAndLock(true);
g_want_determinism = new_want_determinism;
- IOS::HLE::WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism);
+ IOS::HLE::UpdateWantDeterminism(new_want_determinism);
Fifo::UpdateWantDeterminism(new_want_determinism);
// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use
// of FMA.
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index 42496fe3e0..dc6bcd4a36 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -186,16 +186,20 @@
-
-
-
+
4200;%(DisableSpecificWarnings)
+
+ 4200;%(DisableSpecificWarnings)
+
+
+
+
+
+
+
+
-
@@ -419,10 +423,16 @@
+
+
+
+
+
+
-
+
+
-
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index f85e4e64db..109608d2bb 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -773,18 +773,36 @@
IOS\USB
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
IOS\USB
-
+
+ IOS\USB
+
+
IOS\USB
IOS\USB
-
- IOS\USB
-
IOS\WFS
@@ -1353,18 +1371,36 @@
IOS\USB
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
+
+ IOS\USB
+
IOS\USB
-
+
+ IOS\USB
+
+
IOS\USB
IOS\USB
-
- IOS\USB
-
IOS\WFS
diff --git a/Source/Core/Core/IOS/Device.cpp b/Source/Core/Core/IOS/Device.cpp
index dd5de8071f..cf01110caa 100644
--- a/Source/Core/Core/IOS/Device.cpp
+++ b/Source/Core/Core/IOS/Device.cpp
@@ -76,6 +76,16 @@ bool IOCtlVRequest::HasInputVectorWithAddress(const u32 vector_address) const
[&](const auto& in_vector) { return in_vector.address == vector_address; });
}
+bool IOCtlVRequest::HasNumberOfValidVectors(const size_t in_count, const size_t io_count) const
+{
+ if (in_vectors.size() != in_count || io_vectors.size() != io_count)
+ return false;
+
+ auto IsValidVector = [](const auto& vector) { return vector.size == 0 || vector.address != 0; };
+ return std::all_of(in_vectors.begin(), in_vectors.end(), IsValidVector) &&
+ std::all_of(io_vectors.begin(), io_vectors.end(), IsValidVector);
+}
+
void IOCtlRequest::Log(const std::string& device_name, LogTypes::LOG_TYPE type,
LogTypes::LOG_LEVELS verbosity) const
{
diff --git a/Source/Core/Core/IOS/Device.h b/Source/Core/Core/IOS/Device.h
index 23aa9d1dc5..c66665ddae 100644
--- a/Source/Core/Core/IOS/Device.h
+++ b/Source/Core/Core/IOS/Device.h
@@ -42,6 +42,7 @@ enum ReturnCode : s32
FS_EDIRDEPTH = -116, // Max directory depth exceeded
FS_EBUSY = -118, // Resource busy
IPC_EESEXHAUSTED = -1016, // Max of 2 ES handles exceeded
+ USB_ECANCELED = -7022, // USB OH0 insertion hook cancelled
};
struct Request
@@ -122,6 +123,7 @@ struct IOCtlVRequest final : Request
std::vector io_vectors;
explicit IOCtlVRequest(u32 address);
bool HasInputVectorWithAddress(u32 vector_address) const;
+ bool HasNumberOfValidVectors(size_t in_count, size_t io_count) const;
void Dump(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
LogTypes::LOG_LEVELS level = LogTypes::LINFO) const;
void DumpUnknown(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
@@ -137,6 +139,7 @@ public:
{
Static, // Devices which appear in s_device_map.
FileIO, // FileIO devices which are created dynamically.
+ OH0, // OH0 child devices which are created dynamically.
};
Device(u32 device_id, const std::string& device_name, DeviceType type = DeviceType::Static);
@@ -159,6 +162,7 @@ public:
virtual IPCCommandResult IOCtl(const IOCtlRequest& ioctl) { return Unsupported(ioctl); }
virtual IPCCommandResult IOCtlV(const IOCtlVRequest& ioctlv) { return Unsupported(ioctlv); }
virtual void Update() {}
+ virtual void UpdateWantDeterminism(bool new_want_determinism) {}
virtual DeviceType GetDeviceType() const { return m_device_type; }
virtual bool IsOpened() const { return m_is_active; }
static IPCCommandResult GetDefaultReply(s32 return_value);
diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp
index 371942c9eb..1580791a80 100644
--- a/Source/Core/Core/IOS/IPC.cpp
+++ b/Source/Core/Core/IOS/IPC.cpp
@@ -44,12 +44,16 @@
#include "Core/IOS/IPC.h"
#include "Core/IOS/Network/Net.h"
#include "Core/IOS/Network/SSL.h"
+#include "Core/IOS/Network/Socket.h"
#include "Core/IOS/SDIO/SDIOSlot0.h"
#include "Core/IOS/STM/STM.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/BTReal.h"
+#include "Core/IOS/USB/OH0/OH0.h"
+#include "Core/IOS/USB/OH0/OH0Device.h"
+#include "Core/IOS/USB/USB_HID/HIDv4.h"
#include "Core/IOS/USB/USB_KBD.h"
-#include "Core/IOS/USB/USB_VEN.h"
+#include "Core/IOS/USB/USB_VEN/VEN.h"
#include "Core/IOS/WFS/WFSI.h"
#include "Core/IOS/WFS/WFSSRV.h"
@@ -58,10 +62,6 @@ namespace CoreTiming
struct EventType;
} // namespace CoreTiming
-#if defined(__LIBUSB__)
-#include "Core/IOS/USB/USB_HIDv4.h"
-#endif
-
namespace IOS
{
namespace HLE
@@ -85,6 +85,8 @@ static CoreTiming::EventType* s_event_sdio_notify;
static u64 s_last_reply_time;
+static u64 s_active_title_id;
+
static constexpr u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
static constexpr u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
@@ -411,6 +413,14 @@ static void SDIO_EventNotify_CPUThread(u64 userdata, s64 cycles_late)
device->EventNotify();
}
+// The title ID is a u64 where the first 32 bits are used for the title type.
+// For IOS title IDs, the type will always be 00000001 (system), and the lower 32 bits
+// are used for the IOS major version -- which is what we want here.
+u32 GetVersion()
+{
+ return static_cast(s_active_title_id);
+}
+
bool SetupMemory(u64 ios_title_id)
{
auto target_imv = std::find_if(
@@ -423,6 +433,8 @@ bool SetupMemory(u64 ios_title_id)
return false;
}
+ s_active_title_id = ios_title_id;
+
Memory::Write_U32(target_imv->mem1_physical_size, ADDR_MEM1_SIZE);
Memory::Write_U32(target_imv->mem1_simulated_size, ADDR_MEM1_SIM_SIZE);
Memory::Write_U32(target_imv->mem1_end, ADDR_MEM1_END);
@@ -495,15 +507,12 @@ void Reinit()
AddDevice("/dev/net/ip/top");
AddDevice("/dev/net/ssl");
AddDevice("/dev/usb/kbd");
- AddDevice("/dev/usb/ven");
AddDevice("/dev/sdio/slot0");
AddDevice("/dev/sdio/slot1");
-#if defined(__LIBUSB__)
AddDevice("/dev/usb/hid");
-#else
- AddDevice("/dev/usb/hid");
-#endif
+ AddDevice("/dev/usb/oh0");
AddDevice("/dev/usb/oh1");
+ AddDevice("/dev/usb/ven");
AddDevice("/dev/usb/wfssrv");
AddDevice("/dev/wfsi");
}
@@ -644,6 +653,10 @@ void DoState(PointerWrap& p)
s_fdmap[i] = std::make_shared(i, "");
s_fdmap[i]->DoState(p);
break;
+ case Device::Device::DeviceType::OH0:
+ s_fdmap[i] = std::make_shared(i, "");
+ s_fdmap[i]->DoState(p);
+ break;
}
}
}
@@ -710,6 +723,10 @@ static s32 OpenDevice(const OpenRequest& request)
if (!device)
return IPC_EESEXHAUSTED;
}
+ else if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
+ {
+ device = std::make_shared(new_fd, request.path);
+ }
else if (request.path.find("/dev/") == 0)
{
device = GetDeviceByName(request.path);
@@ -851,5 +868,12 @@ void UpdateDevices()
}
}
}
+
+void UpdateWantDeterminism(const bool new_want_determinism)
+{
+ WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism);
+ for (const auto& device : s_device_map)
+ device.second->UpdateWantDeterminism(new_want_determinism);
+}
} // namespace HLE
} // namespace IOS
diff --git a/Source/Core/Core/IOS/IPC.h b/Source/Core/Core/IOS/IPC.h
index 1c6146b793..7508be12f9 100644
--- a/Source/Core/Core/IOS/IPC.h
+++ b/Source/Core/Core/IOS/IPC.h
@@ -51,6 +51,8 @@ void Init();
// Needs to be called after Reset(true) to recreate the device tree
void Reinit();
+u32 GetVersion();
+
bool SetupMemory(u64 ios_title_id);
// Shutdown
@@ -77,6 +79,8 @@ void Update();
// Update Devices
void UpdateDevices();
+void UpdateWantDeterminism(bool new_want_determinism);
+
void ExecuteCommand(u32 address);
void EnqueueRequest(u32 address);
diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp
index 65390e5d49..0e7267b614 100644
--- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp
+++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp
@@ -16,6 +16,7 @@
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonFuncs.h"
+#include "Common/LibusbContext.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/Network.h"
@@ -59,8 +60,8 @@ namespace Device
BluetoothReal::BluetoothReal(u32 device_id, const std::string& device_name)
: BluetoothBase(device_id, device_name)
{
- const int ret = libusb_init(&m_libusb_context);
- _assert_msg_(IOS_WIIMOTE, ret == 0, "Failed to init libusb.");
+ m_libusb_context = LibusbContext::Get();
+ _assert_msg_(IOS_WIIMOTE, m_libusb_context, "Failed to init libusb.");
LoadLinkKeys();
}
@@ -78,15 +79,13 @@ BluetoothReal::~BluetoothReal()
libusb_unref_device(m_device);
}
- libusb_exit(m_libusb_context);
-
SaveLinkKeys();
}
ReturnCode BluetoothReal::Open(const OpenRequest& request)
{
libusb_device** list;
- const ssize_t cnt = libusb_get_device_list(m_libusb_context, &list);
+ const ssize_t cnt = libusb_get_device_list(m_libusb_context.get(), &list);
_dbg_assert_msg_(IOS, cnt > 0, "Couldn't get device list");
for (ssize_t i = 0; i < cnt; ++i)
{
@@ -601,7 +600,7 @@ void BluetoothReal::TransferThread()
Common::SetCurrentThreadName("BT USB Thread");
while (m_thread_running.IsSet())
{
- libusb_handle_events_completed(m_libusb_context, nullptr);
+ libusb_handle_events_completed(m_libusb_context.get(), nullptr);
}
}
diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h
index dc1679925a..ba98483c7c 100644
--- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h
+++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h
@@ -76,7 +76,7 @@ private:
libusb_device* m_device = nullptr;
libusb_device_handle* m_handle = nullptr;
- libusb_context* m_libusb_context = nullptr;
+ std::shared_ptr m_libusb_context;
Common::Flag m_thread_running;
std::thread m_thread;
diff --git a/Source/Core/Core/IOS/USB/Common.cpp b/Source/Core/Core/IOS/USB/Common.cpp
index 12a8891df0..0c1a7317a7 100644
--- a/Source/Core/Core/IOS/USB/Common.cpp
+++ b/Source/Core/Core/IOS/USB/Common.cpp
@@ -2,10 +2,15 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/IOS/USB/Common.h"
+#include
+
+#include "Common/Align.h"
#include "Common/Assert.h"
+#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
+#include "Common/StringUtil.h"
#include "Core/HW/Memmap.h"
+#include "Core/IOS/USB/Common.h"
namespace IOS
{
@@ -26,6 +31,121 @@ void TransferCommand::FillBuffer(const u8* src, const size_t size) const
_assert_msg_(IOS_USB, size == 0 || data_address != 0, "Invalid data_address");
Memory::CopyToEmu(data_address, src, size);
}
+
+void IsoMessage::SetPacketReturnValue(const size_t packet_num, const u16 return_value) const
+{
+ Memory::Write_U16(return_value, static_cast(packet_sizes_addr + packet_num * sizeof(u16)));
+}
+
+Device::~Device() = default;
+
+u64 Device::GetId() const
+{
+ return m_id;
+}
+
+u16 Device::GetVid() const
+{
+ return GetDeviceDescriptor().idVendor;
+}
+
+u16 Device::GetPid() const
+{
+ return GetDeviceDescriptor().idProduct;
+}
+
+bool Device::HasClass(const u8 device_class) const
+{
+ if (GetDeviceDescriptor().bDeviceClass == device_class)
+ return true;
+ const auto interfaces = GetInterfaces(0);
+ return std::any_of(interfaces.begin(), interfaces.end(), [device_class](const auto& interface) {
+ return interface.bInterfaceClass == device_class;
+ });
+}
+
+static void CopyToBufferAligned(std::vector* buffer, const void* data, const size_t size)
+{
+ buffer->insert(buffer->end(), static_cast(data), static_cast(data) + size);
+ const size_t number_of_padding_bytes = Common::AlignUp(size, 4) - size;
+ buffer->insert(buffer->end(), number_of_padding_bytes, 0);
+}
+
+static void CopyDescriptorToBuffer(std::vector* buffer, DeviceDescriptor descriptor)
+{
+ descriptor.bcdUSB = Common::swap16(descriptor.bcdUSB);
+ descriptor.idVendor = Common::swap16(descriptor.idVendor);
+ descriptor.idProduct = Common::swap16(descriptor.idProduct);
+ descriptor.bcdDevice = Common::swap16(descriptor.bcdDevice);
+ CopyToBufferAligned(buffer, &descriptor, descriptor.bLength);
+}
+
+static void CopyDescriptorToBuffer(std::vector* buffer, ConfigDescriptor descriptor)
+{
+ descriptor.wTotalLength = Common::swap16(descriptor.wTotalLength);
+ CopyToBufferAligned(buffer, &descriptor, descriptor.bLength);
+}
+
+static void CopyDescriptorToBuffer(std::vector* buffer, InterfaceDescriptor descriptor)
+{
+ CopyToBufferAligned(buffer, &descriptor, descriptor.bLength);
+}
+
+static void CopyDescriptorToBuffer(std::vector* buffer, EndpointDescriptor descriptor)
+{
+ descriptor.wMaxPacketSize = Common::swap16(descriptor.wMaxPacketSize);
+ // IOS only copies 8 bytes from the endpoint descriptor, regardless of the actual length
+ CopyToBufferAligned(buffer, &descriptor, sizeof(descriptor));
+}
+
+std::vector Device::GetDescriptorsUSBV4() const
+{
+ return GetDescriptors([](const auto& descriptor) { return true; });
+}
+
+std::vector Device::GetDescriptorsUSBV5(const u8 interface, const u8 alt_setting) const
+{
+ return GetDescriptors([interface, alt_setting](const auto& descriptor) {
+ // The USBV5 interfaces present each interface as a different device,
+ // and the descriptors are filtered by alternate setting.
+ return descriptor.bInterfaceNumber == interface && descriptor.bAlternateSetting == alt_setting;
+ });
+}
+
+std::vector
+Device::GetDescriptors(std::function predicate) const
+{
+ std::vector buffer;
+
+ const auto device_descriptor = GetDeviceDescriptor();
+ CopyDescriptorToBuffer(&buffer, device_descriptor);
+
+ const auto configurations = GetConfigurations();
+ for (size_t c = 0; c < configurations.size(); ++c)
+ {
+ const auto& config_descriptor = configurations[c];
+ CopyDescriptorToBuffer(&buffer, config_descriptor);
+
+ const auto interfaces = GetInterfaces(static_cast(c));
+ for (size_t i = interfaces.size(); i-- > 0;)
+ {
+ const auto& descriptor = interfaces[i];
+ if (!predicate(descriptor))
+ continue;
+
+ CopyDescriptorToBuffer(&buffer, descriptor);
+ for (const auto& endpoint_descriptor : GetEndpoints(
+ static_cast(c), descriptor.bInterfaceNumber, descriptor.bAlternateSetting))
+ CopyDescriptorToBuffer(&buffer, endpoint_descriptor);
+ }
+ }
+ return buffer;
+}
+
+std::string Device::GetErrorName(const int error_code) const
+{
+ return StringFromFormat("unknown error %d", error_code);
+}
} // namespace USB
} // namespace HLE
} // namespace IOS
diff --git a/Source/Core/Core/IOS/USB/Common.h b/Source/Core/Core/IOS/USB/Common.h
index 3871ac9e00..6c9bb13913 100644
--- a/Source/Core/Core/IOS/USB/Common.h
+++ b/Source/Core/Core/IOS/USB/Common.h
@@ -5,7 +5,9 @@
#pragma once
#include
+#include
#include
+#include
#include
#include "Common/CommonTypes.h"
@@ -17,6 +19,29 @@ namespace HLE
{
namespace USB
{
+enum StandardDeviceRequestCodes
+{
+ REQUEST_GET_DESCRIPTOR = 6,
+ REQUEST_SET_CONFIGURATION = 9,
+ REQUEST_GET_INTERFACE = 10,
+ REQUEST_SET_INTERFACE = 11,
+};
+
+enum ControlRequestTypes
+{
+ DIR_HOST2DEVICE = 0,
+ DIR_DEVICE2HOST = 1,
+ TYPE_STANDARD = 0,
+ TYPE_VENDOR = 2,
+ REC_DEVICE = 0,
+ REC_INTERFACE = 1,
+};
+
+constexpr u16 USBHDR(u8 dir, u8 type, u8 recipient, u8 request)
+{
+ return static_cast(((dir << 7 | type << 5 | recipient) << 8) | request);
+}
+
struct DeviceDescriptor
{
u8 bLength;
@@ -109,6 +134,49 @@ struct IntrMessage : TransferCommand
u8 endpoint = 0;
using TransferCommand::TransferCommand;
};
+
+struct IsoMessage : TransferCommand
+{
+ u32 packet_sizes_addr = 0;
+ std::vector packet_sizes;
+ u16 length = 0;
+ u8 num_packets = 0;
+ u8 endpoint = 0;
+ using TransferCommand::TransferCommand;
+ void SetPacketReturnValue(size_t packet_num, u16 return_value) const;
+};
+
+class Device
+{
+public:
+ virtual ~Device();
+ u64 GetId() const;
+ u16 GetVid() const;
+ u16 GetPid() const;
+ bool HasClass(u8 device_class) const;
+ std::vector GetDescriptorsUSBV4() const;
+ std::vector GetDescriptorsUSBV5(u8 interface, u8 alt_setting) const;
+
+ virtual DeviceDescriptor GetDeviceDescriptor() const = 0;
+ virtual std::vector GetConfigurations() const = 0;
+ virtual std::vector GetInterfaces(u8 config) const = 0;
+ virtual std::vector GetEndpoints(u8 config, u8 interface, u8 alt) const = 0;
+
+ virtual std::string GetErrorName(int error_code) const;
+ virtual bool Attach(u8 interface) = 0;
+ virtual int CancelTransfer(u8 endpoint) = 0;
+ virtual int ChangeInterface(u8 interface) = 0;
+ virtual int GetNumberOfAltSettings(u8 interface) = 0;
+ virtual int SetAltSetting(u8 alt_setting) = 0;
+ virtual int SubmitTransfer(std::unique_ptr message) = 0;
+ virtual int SubmitTransfer(std::unique_ptr message) = 0;
+ virtual int SubmitTransfer(std::unique_ptr message) = 0;
+ virtual int SubmitTransfer(std::unique_ptr message) = 0;
+
+protected:
+ std::vector GetDescriptors(std::function predicate) const;
+ u64 m_id = 0xFFFFFFFFFFFFFFFF;
+};
} // namespace USB
} // namespace HLE
} // namespace IOS
diff --git a/Source/Core/Core/IOS/USB/Host.cpp b/Source/Core/Core/IOS/USB/Host.cpp
new file mode 100644
index 0000000000..875dc451f1
--- /dev/null
+++ b/Source/Core/Core/IOS/USB/Host.cpp
@@ -0,0 +1,244 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+#include
+#include
+
+#ifdef __LIBUSB__
+#include
+#endif
+
+#include "Common/Assert.h"
+#include "Common/ChunkFile.h"
+#include "Common/CommonTypes.h"
+#include "Common/LibusbContext.h"
+#include "Common/Logging/Log.h"
+#include "Common/Thread.h"
+#include "Core/ConfigManager.h"
+#include "Core/Core.h"
+#include "Core/IOS/USB/Common.h"
+#include "Core/IOS/USB/Host.h"
+#include "Core/IOS/USB/LibusbDevice.h"
+
+namespace IOS
+{
+namespace HLE
+{
+namespace Device
+{
+USBHost::USBHost(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
+{
+#ifdef __LIBUSB__
+ m_libusb_context = LibusbContext::Get();
+ _assert_msg_(IOS_USB, m_libusb_context, "Failed to init libusb.");
+#endif
+}
+
+ReturnCode 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).
+ while (!UpdateDevices())
+ {
+ }
+ StartThreads();
+ return IPC_SUCCESS;
+}
+
+void USBHost::UpdateWantDeterminism(const bool new_want_determinism)
+{
+ if (new_want_determinism)
+ StopThreads();
+ else if (IsOpened())
+ StartThreads();
+}
+
+void USBHost::DoState(PointerWrap& p)
+{
+ if (IsOpened() && p.GetMode() == PointerWrap::MODE_READ)
+ {
+ // After a state has loaded, there may be insertion hooks for devices that were
+ // already plugged in, and which need to be triggered.
+ UpdateDevices(true);
+ }
+}
+
+bool USBHost::AddDevice(std::unique_ptr device)
+{
+ std::lock_guard lk(m_devices_mutex);
+ if (m_devices.find(device->GetId()) != m_devices.end())
+ return false;
+
+ m_devices[device->GetId()] = std::move(device);
+ return true;
+}
+
+std::shared_ptr USBHost::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;
+}
+
+void USBHost::OnDeviceChange(ChangeEvent event, std::shared_ptr changed_device)
+{
+}
+
+void USBHost::OnDeviceChangeEnd()
+{
+}
+
+bool USBHost::ShouldAddDevice(const USB::Device& device) const
+{
+ return true;
+}
+
+// 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)
+{
+ if (Core::g_want_determinism)
+ return true;
+
+ 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))
+ return false;
+ DetectRemovedDevices(plugged_devices, hooks);
+ DispatchHooks(hooks);
+ return true;
+}
+
+bool USBHost::AddNewDevices(std::set& new_devices, DeviceChangeHooks& hooks,
+ const bool always_add_hooks)
+{
+#ifdef __LIBUSB__
+ libusb_device** list;
+ const ssize_t count = libusb_get_device_list(m_libusb_context.get(), &list);
+ if (count < 0)
+ {
+ WARN_LOG(IOS_USB, "Failed to get device list: %s", libusb_error_name(static_cast(count)));
+ return false;
+ }
+
+ for (ssize_t i = 0; i < count; ++i)
+ {
+ libusb_device* device = list[i];
+ libusb_device_descriptor descriptor;
+ libusb_get_device_descriptor(device, &descriptor);
+ if (!SConfig::GetInstance().IsUSBDeviceWhitelisted({descriptor.idVendor, descriptor.idProduct}))
+ continue;
+
+ auto usb_device = std::make_unique(device, descriptor);
+ if (!ShouldAddDevice(*usb_device))
+ continue;
+ const u64 id = usb_device->GetId();
+ new_devices.insert(id);
+ if (AddDevice(std::move(usb_device)) || always_add_hooks)
+ hooks.emplace(GetDeviceById(id), ChangeEvent::Inserted);
+ }
+ libusb_free_device_list(list, 1);
+#endif
+ return true;
+}
+
+void USBHost::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.find(it->second->GetId()) == plugged_devices.end())
+ {
+ hooks.emplace(it->second, ChangeEvent::Removed);
+ it = m_devices.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+void USBHost::DispatchHooks(const DeviceChangeHooks& hooks)
+{
+ for (const auto& hook : hooks)
+ {
+ INFO_LOG(IOS_USB, "%s - %s device: %04x:%04x", GetDeviceName().c_str(),
+ hook.second == ChangeEvent::Inserted ? "New" : "Removed", hook.first->GetVid(),
+ hook.first->GetPid());
+ OnDeviceChange(hook.second, hook.first);
+ }
+ if (!hooks.empty())
+ OnDeviceChangeEnd();
+}
+
+void USBHost::StartThreads()
+{
+ if (Core::g_want_determinism)
+ return;
+
+ if (!m_scan_thread_running.IsSet())
+ {
+ m_scan_thread_running.Set();
+ m_scan_thread = std::thread([this] {
+ Common::SetCurrentThreadName("USB Scan Thread");
+ while (m_scan_thread_running.IsSet())
+ {
+ UpdateDevices();
+ Common::SleepCurrentThread(50);
+ }
+ });
+ }
+
+#ifdef __LIBUSB__
+ if (!m_event_thread_running.IsSet())
+ {
+ m_event_thread_running.Set();
+ m_event_thread = std::thread([this] {
+ Common::SetCurrentThreadName("USB Passthrough Thread");
+ while (m_event_thread_running.IsSet())
+ {
+ static timeval tv = {0, 50000};
+ libusb_handle_events_timeout_completed(m_libusb_context.get(), &tv, nullptr);
+ }
+ });
+ }
+#endif
+}
+
+void USBHost::StopThreads()
+{
+ if (m_scan_thread_running.TestAndClear())
+ m_scan_thread.join();
+
+ // Clear all devices and dispatch removal hooks.
+ DeviceChangeHooks hooks;
+ DetectRemovedDevices(std::set(), hooks);
+ DispatchHooks(hooks);
+#ifdef __LIBUSB__
+ if (m_event_thread_running.TestAndClear())
+ m_event_thread.join();
+#endif
+}
+
+IPCCommandResult USBHost::HandleTransfer(std::shared_ptr device, u32 request,
+ std::function submit) const
+{
+ if (!device)
+ return GetDefaultReply(IPC_ENOENT);
+
+ const s32 ret = submit();
+ if (ret == IPC_SUCCESS)
+ return GetNoReply();
+
+ ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to submit transfer (request %u): %s", device->GetVid(),
+ device->GetPid(), request, device->GetErrorName(ret).c_str());
+ return GetDefaultReply(ret <= 0 ? ret : IPC_EINVAL);
+}
+} // namespace Device
+} // namespace HLE
+} // namespace IOS
diff --git a/Source/Core/Core/IOS/USB/Host.h b/Source/Core/Core/IOS/USB/Host.h
new file mode 100644
index 0000000000..40c09ca709
--- /dev/null
+++ b/Source/Core/Core/IOS/USB/Host.h
@@ -0,0 +1,85 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include