mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-24 15:01:16 +01:00
Merge pull request #4408 from leoetlino/usb
IOS: USB support (OH0, USB_VEN, USB_HID)
This commit is contained in:
commit
d244597b42
4
Externals/libusb/libusb_static_2013.vcxproj
vendored
4
Externals/libusb/libusb_static_2013.vcxproj
vendored
@ -52,7 +52,7 @@
|
|||||||
<ClCompile Include="libusb\sync.c" />
|
<ClCompile Include="libusb\sync.c" />
|
||||||
<ClCompile Include="libusb\os\threads_windows.c" />
|
<ClCompile Include="libusb\os\threads_windows.c" />
|
||||||
<ClCompile Include="libusb\os\windows_nt_common.c" />
|
<ClCompile Include="libusb\os\windows_nt_common.c" />
|
||||||
<ClCompile Include="libusb\os\windows_winusb.c" />
|
<ClCompile Include="libusb\os\windows_usbdk.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="msvc\config.h" />
|
<ClInclude Include="msvc\config.h" />
|
||||||
@ -65,7 +65,7 @@
|
|||||||
<ClInclude Include="libusb\version_nano.h" />
|
<ClInclude Include="libusb\version_nano.h" />
|
||||||
<ClInclude Include="libusb\os\windows_common.h" />
|
<ClInclude Include="libusb\os\windows_common.h" />
|
||||||
<ClInclude Include="libusb\os\windows_nt_common.h" />
|
<ClInclude Include="libusb\os\windows_nt_common.h" />
|
||||||
<ClInclude Include="libusb\os\windows_winusb.h" />
|
<ClInclude Include="libusb\os\windows_usbdk.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
@ -34,6 +34,11 @@ set(SRCS Analytics.cpp
|
|||||||
Crypto/ec.cpp
|
Crypto/ec.cpp
|
||||||
Logging/LogManager.cpp)
|
Logging/LogManager.cpp)
|
||||||
|
|
||||||
|
if(LIBUSB_FOUND)
|
||||||
|
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
|
||||||
|
set(SRCS ${SRCS} LibusbContext.cpp)
|
||||||
|
endif(LIBUSB_FOUND)
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
set(SRCS ${SRCS}
|
set(SRCS ${SRCS}
|
||||||
Logging/ConsoleListenerDroid.cpp)
|
Logging/ConsoleListenerDroid.cpp)
|
||||||
|
@ -113,6 +113,7 @@
|
|||||||
<ClInclude Include="Hash.h" />
|
<ClInclude Include="Hash.h" />
|
||||||
<ClInclude Include="IniFile.h" />
|
<ClInclude Include="IniFile.h" />
|
||||||
<ClInclude Include="JitRegister.h" />
|
<ClInclude Include="JitRegister.h" />
|
||||||
|
<ClInclude Include="LibusbContext.h" />
|
||||||
<ClInclude Include="LinearDiskCache.h" />
|
<ClInclude Include="LinearDiskCache.h" />
|
||||||
<ClInclude Include="MathUtil.h" />
|
<ClInclude Include="MathUtil.h" />
|
||||||
<ClInclude Include="MD5.h" />
|
<ClInclude Include="MD5.h" />
|
||||||
@ -159,6 +160,9 @@
|
|||||||
<ClCompile Include="Hash.cpp" />
|
<ClCompile Include="Hash.cpp" />
|
||||||
<ClCompile Include="IniFile.cpp" />
|
<ClCompile Include="IniFile.cpp" />
|
||||||
<ClCompile Include="JitRegister.cpp" />
|
<ClCompile Include="JitRegister.cpp" />
|
||||||
|
<ClCompile Include="LibusbContext.cpp">
|
||||||
|
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="Logging\ConsoleListenerWin.cpp" />
|
<ClCompile Include="Logging\ConsoleListenerWin.cpp" />
|
||||||
<ClCompile Include="MathUtil.cpp" />
|
<ClCompile Include="MathUtil.cpp" />
|
||||||
<ClCompile Include="MD5.cpp" />
|
<ClCompile Include="MD5.cpp" />
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
<ClInclude Include="FPURoundMode.h" />
|
<ClInclude Include="FPURoundMode.h" />
|
||||||
<ClInclude Include="Hash.h" />
|
<ClInclude Include="Hash.h" />
|
||||||
<ClInclude Include="IniFile.h" />
|
<ClInclude Include="IniFile.h" />
|
||||||
|
<ClInclude Include="LibusbContext.h" />
|
||||||
<ClInclude Include="LinearDiskCache.h" />
|
<ClInclude Include="LinearDiskCache.h" />
|
||||||
<ClInclude Include="MathUtil.h" />
|
<ClInclude Include="MathUtil.h" />
|
||||||
<ClInclude Include="MemArena.h" />
|
<ClInclude Include="MemArena.h" />
|
||||||
@ -236,6 +237,7 @@
|
|||||||
<ClCompile Include="FileUtil.cpp" />
|
<ClCompile Include="FileUtil.cpp" />
|
||||||
<ClCompile Include="Hash.cpp" />
|
<ClCompile Include="Hash.cpp" />
|
||||||
<ClCompile Include="IniFile.cpp" />
|
<ClCompile Include="IniFile.cpp" />
|
||||||
|
<ClCompile Include="LibusbContext.cpp" />
|
||||||
<ClCompile Include="MathUtil.cpp" />
|
<ClCompile Include="MathUtil.cpp" />
|
||||||
<ClCompile Include="MemArena.cpp" />
|
<ClCompile Include="MemArena.cpp" />
|
||||||
<ClCompile Include="MemoryUtil.cpp" />
|
<ClCompile Include="MemoryUtil.cpp" />
|
||||||
|
46
Source/Core/Common/LibusbContext.cpp
Normal file
46
Source/Core/Common/LibusbContext.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "Common/LibusbContext.h"
|
||||||
|
#include "Common/MsgHandler.h"
|
||||||
|
|
||||||
|
namespace LibusbContext
|
||||||
|
{
|
||||||
|
static std::shared_ptr<libusb_context> 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<libusb_context> Get()
|
||||||
|
{
|
||||||
|
std::call_once(s_context_initialized, []() {
|
||||||
|
s_libusb_context.reset(Create(), [](auto* context) {
|
||||||
|
if (context != nullptr)
|
||||||
|
libusb_exit(context);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return s_libusb_context;
|
||||||
|
}
|
||||||
|
}
|
15
Source/Core/Common/LibusbContext.h
Normal file
15
Source/Core/Common/LibusbContext.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
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<libusb_context> Get();
|
||||||
|
}
|
@ -31,7 +31,6 @@ enum LOG_TYPE
|
|||||||
IOS_DI,
|
IOS_DI,
|
||||||
IOS_ES,
|
IOS_ES,
|
||||||
IOS_FILEIO,
|
IOS_FILEIO,
|
||||||
IOS_HID,
|
|
||||||
IOS_NET,
|
IOS_NET,
|
||||||
IOS_SD,
|
IOS_SD,
|
||||||
IOS_SSL,
|
IOS_SSL,
|
||||||
|
@ -65,7 +65,6 @@ LogManager::LogManager()
|
|||||||
m_Log[LogTypes::IOS_DI] = new LogContainer("IOS_DI", "IOS - Drive Interface");
|
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_ES] = new LogContainer("IOS_ES", "IOS - ETicket Services");
|
||||||
m_Log[LogTypes::IOS_FILEIO] = new LogContainer("IOS_FILEIO", "IOS - FileIO");
|
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_SD] = new LogContainer("IOS_SD", "IOS - SDIO");
|
||||||
m_Log[LogTypes::IOS_SSL] = new LogContainer("IOS_SSL", "IOS - SSL");
|
m_Log[LogTypes::IOS_SSL] = new LogContainer("IOS_SSL", "IOS - SSL");
|
||||||
m_Log[LogTypes::IOS_STM] = new LogContainer("IOS_STM", "IOS - State Transition Manager");
|
m_Log[LogTypes::IOS_STM] = new LogContainer("IOS_STM", "IOS - State Transition Manager");
|
||||||
|
@ -154,9 +154,15 @@ set(SRCS ActionReplay.cpp
|
|||||||
IOS/SDIO/SDIOSlot0.cpp
|
IOS/SDIO/SDIOSlot0.cpp
|
||||||
IOS/STM/STM.cpp
|
IOS/STM/STM.cpp
|
||||||
IOS/USB/Common.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/USBV0.cpp
|
||||||
|
IOS/USB/USBV4.cpp
|
||||||
|
IOS/USB/USBV5.cpp
|
||||||
IOS/USB/USB_KBD.cpp
|
IOS/USB/USB_KBD.cpp
|
||||||
IOS/USB/USB_VEN.cpp
|
|
||||||
IOS/USB/Bluetooth/BTBase.cpp
|
IOS/USB/Bluetooth/BTBase.cpp
|
||||||
IOS/USB/Bluetooth/BTEmu.cpp
|
IOS/USB/Bluetooth/BTEmu.cpp
|
||||||
IOS/USB/Bluetooth/BTStub.cpp
|
IOS/USB/Bluetooth/BTStub.cpp
|
||||||
@ -264,7 +270,7 @@ set(LIBS
|
|||||||
if(LIBUSB_FOUND)
|
if(LIBUSB_FOUND)
|
||||||
# Using shared LibUSB
|
# Using shared LibUSB
|
||||||
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
|
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)
|
IOS/USB/Bluetooth/BTReal.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ void SConfig::SaveSettings()
|
|||||||
SaveAnalyticsSettings(ini);
|
SaveAnalyticsSettings(ini);
|
||||||
SaveNetworkSettings(ini);
|
SaveNetworkSettings(ini);
|
||||||
SaveBluetoothPassthroughSettings(ini);
|
SaveBluetoothPassthroughSettings(ini);
|
||||||
|
SaveUSBPassthroughSettings(ini);
|
||||||
SaveSysconfSettings(ini);
|
SaveSysconfSettings(ini);
|
||||||
|
|
||||||
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||||
@ -343,6 +344,20 @@ void SConfig::SaveBluetoothPassthroughSettings(IniFile& ini)
|
|||||||
section->Set("LinkKeys", m_bt_passthrough_link_keys);
|
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)
|
void SConfig::SaveSysconfSettings(IniFile& ini)
|
||||||
{
|
{
|
||||||
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
|
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
|
||||||
@ -400,6 +415,7 @@ void SConfig::LoadSettings()
|
|||||||
LoadNetworkSettings(ini);
|
LoadNetworkSettings(ini);
|
||||||
LoadAnalyticsSettings(ini);
|
LoadAnalyticsSettings(ini);
|
||||||
LoadBluetoothPassthroughSettings(ini);
|
LoadBluetoothPassthroughSettings(ini);
|
||||||
|
LoadUSBPassthroughSettings(ini);
|
||||||
LoadSysconfSettings(ini);
|
LoadSysconfSettings(ini);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,6 +682,27 @@ void SConfig::LoadBluetoothPassthroughSettings(IniFile& ini)
|
|||||||
section->Get("LinkKeys", &m_bt_passthrough_link_keys, "");
|
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<std::string> 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<u16>(strtol(pair.substr(0, index).c_str(), nullptr, 16));
|
||||||
|
const u16 pid = static_cast<u16>(strtol(pair.substr(index + 1).c_str(), nullptr, 16));
|
||||||
|
if (vid && pid)
|
||||||
|
m_usb_passthrough_devices.emplace(vid, pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SConfig::LoadSysconfSettings(IniFile& ini)
|
void SConfig::LoadSysconfSettings(IniFile& ini)
|
||||||
{
|
{
|
||||||
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
|
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
|
||||||
@ -757,6 +794,11 @@ void SConfig::LoadDefaults()
|
|||||||
m_revision = 0;
|
m_revision = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SConfig::IsUSBDeviceWhitelisted(const std::pair<u16, u16> vid_pid) const
|
||||||
|
{
|
||||||
|
return m_usb_passthrough_devices.find(vid_pid) != m_usb_passthrough_devices.end();
|
||||||
|
}
|
||||||
|
|
||||||
const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
|
const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
|
||||||
{
|
{
|
||||||
switch (region)
|
switch (region)
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/IniFile.h"
|
#include "Common/IniFile.h"
|
||||||
@ -151,6 +153,10 @@ struct SConfig : NonCopyable
|
|||||||
int m_bt_passthrough_vid = -1;
|
int m_bt_passthrough_vid = -1;
|
||||||
std::string m_bt_passthrough_link_keys;
|
std::string m_bt_passthrough_link_keys;
|
||||||
|
|
||||||
|
// USB passthrough settings
|
||||||
|
std::set<std::pair<u16, u16>> m_usb_passthrough_devices;
|
||||||
|
bool IsUSBDeviceWhitelisted(std::pair<u16, u16> vid_pid) const;
|
||||||
|
|
||||||
// SYSCONF settings
|
// SYSCONF settings
|
||||||
int m_sensor_bar_position = 0x01;
|
int m_sensor_bar_position = 0x01;
|
||||||
int m_sensor_bar_sensitivity = 0x03;
|
int m_sensor_bar_sensitivity = 0x03;
|
||||||
@ -351,6 +357,7 @@ private:
|
|||||||
void SaveNetworkSettings(IniFile& ini);
|
void SaveNetworkSettings(IniFile& ini);
|
||||||
void SaveAnalyticsSettings(IniFile& ini);
|
void SaveAnalyticsSettings(IniFile& ini);
|
||||||
void SaveBluetoothPassthroughSettings(IniFile& ini);
|
void SaveBluetoothPassthroughSettings(IniFile& ini);
|
||||||
|
void SaveUSBPassthroughSettings(IniFile& ini);
|
||||||
void SaveSysconfSettings(IniFile& ini);
|
void SaveSysconfSettings(IniFile& ini);
|
||||||
|
|
||||||
void LoadGeneralSettings(IniFile& ini);
|
void LoadGeneralSettings(IniFile& ini);
|
||||||
@ -365,6 +372,7 @@ private:
|
|||||||
void LoadNetworkSettings(IniFile& ini);
|
void LoadNetworkSettings(IniFile& ini);
|
||||||
void LoadAnalyticsSettings(IniFile& ini);
|
void LoadAnalyticsSettings(IniFile& ini);
|
||||||
void LoadBluetoothPassthroughSettings(IniFile& ini);
|
void LoadBluetoothPassthroughSettings(IniFile& ini);
|
||||||
|
void LoadUSBPassthroughSettings(IniFile& ini);
|
||||||
void LoadSysconfSettings(IniFile& ini);
|
void LoadSysconfSettings(IniFile& ini);
|
||||||
|
|
||||||
bool SetRegion(DiscIO::Region region, std::string* directory_name);
|
bool SetRegion(DiscIO::Region region, std::string* directory_name);
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/HW/VideoInterface.h"
|
#include "Core/HW/VideoInterface.h"
|
||||||
#include "Core/HW/Wiimote.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/BTEmu.h"
|
||||||
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
@ -975,7 +975,7 @@ void UpdateWantDeterminism(bool initial)
|
|||||||
bool was_unpaused = Core::PauseAndLock(true);
|
bool was_unpaused = Core::PauseAndLock(true);
|
||||||
|
|
||||||
g_want_determinism = new_want_determinism;
|
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);
|
Fifo::UpdateWantDeterminism(new_want_determinism);
|
||||||
// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use
|
// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use
|
||||||
// of FMA.
|
// of FMA.
|
||||||
|
@ -186,16 +186,20 @@
|
|||||||
<ClCompile Include="IOS\SDIO\SDIOSlot0.cpp" />
|
<ClCompile Include="IOS\SDIO\SDIOSlot0.cpp" />
|
||||||
<ClCompile Include="IOS\STM\STM.cpp" />
|
<ClCompile Include="IOS\STM\STM.cpp" />
|
||||||
<ClCompile Include="IOS\USB\Common.cpp" />
|
<ClCompile Include="IOS\USB\Common.cpp" />
|
||||||
<ClCompile Include="IOS\USB\USBV0.cpp" />
|
<ClCompile Include="IOS\USB\LibusbDevice.cpp">
|
||||||
<ClCompile Include="IOS\USB\USB_HIDv4.cpp">
|
|
||||||
<!--
|
|
||||||
Disable "nonstandard extension used : zero-sized array in struct/union" warning,
|
|
||||||
which is hit in libusb.h.
|
|
||||||
-->
|
|
||||||
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\Host.cpp">
|
||||||
|
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\OH0\OH0.cpp" />
|
||||||
|
<ClCompile Include="IOS\USB\OH0\OH0Device.cpp" />
|
||||||
|
<ClCompile Include="IOS\USB\USB_HID\HIDv4.cpp" />
|
||||||
|
<ClCompile Include="IOS\USB\USB_VEN\VEN.cpp" />
|
||||||
|
<ClCompile Include="IOS\USB\USBV0.cpp" />
|
||||||
|
<ClCompile Include="IOS\USB\USBV4.cpp" />
|
||||||
|
<ClCompile Include="IOS\USB\USBV5.cpp" />
|
||||||
<ClCompile Include="IOS\USB\USB_KBD.cpp" />
|
<ClCompile Include="IOS\USB\USB_KBD.cpp" />
|
||||||
<ClCompile Include="IOS\USB\USB_VEN.cpp" />
|
|
||||||
<ClCompile Include="IOS\USB\Bluetooth\BTBase.cpp" />
|
<ClCompile Include="IOS\USB\Bluetooth\BTBase.cpp" />
|
||||||
<ClCompile Include="IOS\USB\Bluetooth\BTEmu.cpp" />
|
<ClCompile Include="IOS\USB\Bluetooth\BTEmu.cpp" />
|
||||||
<ClCompile Include="IOS\USB\Bluetooth\BTStub.cpp" />
|
<ClCompile Include="IOS\USB\Bluetooth\BTStub.cpp" />
|
||||||
@ -419,10 +423,16 @@
|
|||||||
<ClInclude Include="IOS\SDIO\SDIOSlot0.h" />
|
<ClInclude Include="IOS\SDIO\SDIOSlot0.h" />
|
||||||
<ClInclude Include="IOS\STM\STM.h" />
|
<ClInclude Include="IOS\STM\STM.h" />
|
||||||
<ClInclude Include="IOS\USB\Common.h" />
|
<ClInclude Include="IOS\USB\Common.h" />
|
||||||
|
<ClInclude Include="IOS\USB\LibusbDevice.h" />
|
||||||
|
<ClInclude Include="IOS\USB\Host.h" />
|
||||||
|
<ClInclude Include="IOS\USB\OH0\OH0.h" />
|
||||||
|
<ClInclude Include="IOS\USB\OH0\OH0Device.h" />
|
||||||
|
<ClInclude Include="IOS\USB\USB_HID\HIDv4.h" />
|
||||||
|
<ClInclude Include="IOS\USB\USB_VEN\VEN.h" />
|
||||||
<ClInclude Include="IOS\USB\USBV0.h" />
|
<ClInclude Include="IOS\USB\USBV0.h" />
|
||||||
<ClInclude Include="IOS\USB\USB_HIDv4.h" />
|
<ClInclude Include="IOS\USB\USBV4.h" />
|
||||||
|
<ClInclude Include="IOS\USB\USBV5.h" />
|
||||||
<ClInclude Include="IOS\USB\USB_KBD.h" />
|
<ClInclude Include="IOS\USB\USB_KBD.h" />
|
||||||
<ClInclude Include="IOS\USB\USB_VEN.h" />
|
|
||||||
<ClInclude Include="IOS\USB\Bluetooth\BTBase.h" />
|
<ClInclude Include="IOS\USB\Bluetooth\BTBase.h" />
|
||||||
<ClInclude Include="IOS\USB\Bluetooth\BTEmu.h" />
|
<ClInclude Include="IOS\USB\Bluetooth\BTEmu.h" />
|
||||||
<ClInclude Include="IOS\USB\Bluetooth\BTStub.h" />
|
<ClInclude Include="IOS\USB\Bluetooth\BTStub.h" />
|
||||||
|
@ -773,18 +773,36 @@
|
|||||||
<ClCompile Include="IOS\USB\Common.cpp">
|
<ClCompile Include="IOS\USB\Common.cpp">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\LibusbDevice.cpp">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\Host.cpp">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\OH0\OH0.cpp">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\OH0\OH0Device.cpp">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\USB_HID\HIDv4.cpp">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\USB_VEN\VEN.cpp">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="IOS\USB\USBV0.cpp">
|
<ClCompile Include="IOS\USB\USBV0.cpp">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="IOS\USB\USB_HIDv4.cpp">
|
<ClCompile Include="IOS\USB\USBV4.cpp">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\USB\USBV5.cpp">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="IOS\USB\USB_KBD.cpp">
|
<ClCompile Include="IOS\USB\USB_KBD.cpp">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="IOS\USB\USB_VEN.cpp">
|
|
||||||
<Filter>IOS\USB</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="IOS\WFS\WFSI.cpp">
|
<ClCompile Include="IOS\WFS\WFSI.cpp">
|
||||||
<Filter>IOS\WFS</Filter>
|
<Filter>IOS\WFS</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -1353,18 +1371,36 @@
|
|||||||
<ClInclude Include="IOS\USB\Common.h">
|
<ClInclude Include="IOS\USB\Common.h">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\USB\LibusbDevice.h">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\USB\Host.h">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\USB\OH0\OH0.h">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\USB\OH0\OH0Device.h">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\USB\USB_HID\HIDv4.h">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\USB\USB_VEN\VEN.h">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="IOS\USB\USBV0.h">
|
<ClInclude Include="IOS\USB\USBV0.h">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="IOS\USB\USB_HIDv4.h">
|
<ClInclude Include="IOS\USB\USBV4.h">
|
||||||
|
<Filter>IOS\USB</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\USB\USBV5.h">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="IOS\USB\USB_KBD.h">
|
<ClInclude Include="IOS\USB\USB_KBD.h">
|
||||||
<Filter>IOS\USB</Filter>
|
<Filter>IOS\USB</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="IOS\USB\USB_VEN.h">
|
|
||||||
<Filter>IOS\USB</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="IOS\WFS\WFSI.h">
|
<ClInclude Include="IOS\WFS\WFSI.h">
|
||||||
<Filter>IOS\WFS</Filter>
|
<Filter>IOS\WFS</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -76,6 +76,16 @@ bool IOCtlVRequest::HasInputVectorWithAddress(const u32 vector_address) const
|
|||||||
[&](const auto& in_vector) { return in_vector.address == vector_address; });
|
[&](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,
|
void IOCtlRequest::Log(const std::string& device_name, LogTypes::LOG_TYPE type,
|
||||||
LogTypes::LOG_LEVELS verbosity) const
|
LogTypes::LOG_LEVELS verbosity) const
|
||||||
{
|
{
|
||||||
|
@ -42,6 +42,7 @@ enum ReturnCode : s32
|
|||||||
FS_EDIRDEPTH = -116, // Max directory depth exceeded
|
FS_EDIRDEPTH = -116, // Max directory depth exceeded
|
||||||
FS_EBUSY = -118, // Resource busy
|
FS_EBUSY = -118, // Resource busy
|
||||||
IPC_EESEXHAUSTED = -1016, // Max of 2 ES handles exceeded
|
IPC_EESEXHAUSTED = -1016, // Max of 2 ES handles exceeded
|
||||||
|
USB_ECANCELED = -7022, // USB OH0 insertion hook cancelled
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Request
|
struct Request
|
||||||
@ -122,6 +123,7 @@ struct IOCtlVRequest final : Request
|
|||||||
std::vector<IOVector> io_vectors;
|
std::vector<IOVector> io_vectors;
|
||||||
explicit IOCtlVRequest(u32 address);
|
explicit IOCtlVRequest(u32 address);
|
||||||
bool HasInputVectorWithAddress(u32 vector_address) const;
|
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,
|
void Dump(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
|
||||||
LogTypes::LOG_LEVELS level = LogTypes::LINFO) const;
|
LogTypes::LOG_LEVELS level = LogTypes::LINFO) const;
|
||||||
void DumpUnknown(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
|
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.
|
Static, // Devices which appear in s_device_map.
|
||||||
FileIO, // FileIO devices which are created dynamically.
|
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);
|
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 IOCtl(const IOCtlRequest& ioctl) { return Unsupported(ioctl); }
|
||||||
virtual IPCCommandResult IOCtlV(const IOCtlVRequest& ioctlv) { return Unsupported(ioctlv); }
|
virtual IPCCommandResult IOCtlV(const IOCtlVRequest& ioctlv) { return Unsupported(ioctlv); }
|
||||||
virtual void Update() {}
|
virtual void Update() {}
|
||||||
|
virtual void UpdateWantDeterminism(bool new_want_determinism) {}
|
||||||
virtual DeviceType GetDeviceType() const { return m_device_type; }
|
virtual DeviceType GetDeviceType() const { return m_device_type; }
|
||||||
virtual bool IsOpened() const { return m_is_active; }
|
virtual bool IsOpened() const { return m_is_active; }
|
||||||
static IPCCommandResult GetDefaultReply(s32 return_value);
|
static IPCCommandResult GetDefaultReply(s32 return_value);
|
||||||
|
@ -44,12 +44,16 @@
|
|||||||
#include "Core/IOS/IPC.h"
|
#include "Core/IOS/IPC.h"
|
||||||
#include "Core/IOS/Network/Net.h"
|
#include "Core/IOS/Network/Net.h"
|
||||||
#include "Core/IOS/Network/SSL.h"
|
#include "Core/IOS/Network/SSL.h"
|
||||||
|
#include "Core/IOS/Network/Socket.h"
|
||||||
#include "Core/IOS/SDIO/SDIOSlot0.h"
|
#include "Core/IOS/SDIO/SDIOSlot0.h"
|
||||||
#include "Core/IOS/STM/STM.h"
|
#include "Core/IOS/STM/STM.h"
|
||||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||||
#include "Core/IOS/USB/Bluetooth/BTReal.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_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/WFSI.h"
|
||||||
#include "Core/IOS/WFS/WFSSRV.h"
|
#include "Core/IOS/WFS/WFSSRV.h"
|
||||||
|
|
||||||
@ -58,10 +62,6 @@ namespace CoreTiming
|
|||||||
struct EventType;
|
struct EventType;
|
||||||
} // namespace CoreTiming
|
} // namespace CoreTiming
|
||||||
|
|
||||||
#if defined(__LIBUSB__)
|
|
||||||
#include "Core/IOS/USB/USB_HIDv4.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
namespace HLE
|
namespace HLE
|
||||||
@ -85,6 +85,8 @@ static CoreTiming::EventType* s_event_sdio_notify;
|
|||||||
|
|
||||||
static u64 s_last_reply_time;
|
static u64 s_last_reply_time;
|
||||||
|
|
||||||
|
static u64 s_active_title_id;
|
||||||
|
|
||||||
static constexpr u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
|
static constexpr u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
|
||||||
static constexpr u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
|
static constexpr u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
|
||||||
|
|
||||||
@ -411,6 +413,14 @@ static void SDIO_EventNotify_CPUThread(u64 userdata, s64 cycles_late)
|
|||||||
device->EventNotify();
|
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<u32>(s_active_title_id);
|
||||||
|
}
|
||||||
|
|
||||||
bool SetupMemory(u64 ios_title_id)
|
bool SetupMemory(u64 ios_title_id)
|
||||||
{
|
{
|
||||||
auto target_imv = std::find_if(
|
auto target_imv = std::find_if(
|
||||||
@ -423,6 +433,8 @@ bool SetupMemory(u64 ios_title_id)
|
|||||||
return false;
|
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_physical_size, ADDR_MEM1_SIZE);
|
||||||
Memory::Write_U32(target_imv->mem1_simulated_size, ADDR_MEM1_SIM_SIZE);
|
Memory::Write_U32(target_imv->mem1_simulated_size, ADDR_MEM1_SIM_SIZE);
|
||||||
Memory::Write_U32(target_imv->mem1_end, ADDR_MEM1_END);
|
Memory::Write_U32(target_imv->mem1_end, ADDR_MEM1_END);
|
||||||
@ -495,15 +507,12 @@ void Reinit()
|
|||||||
AddDevice<Device::NetIPTop>("/dev/net/ip/top");
|
AddDevice<Device::NetIPTop>("/dev/net/ip/top");
|
||||||
AddDevice<Device::NetSSL>("/dev/net/ssl");
|
AddDevice<Device::NetSSL>("/dev/net/ssl");
|
||||||
AddDevice<Device::USB_KBD>("/dev/usb/kbd");
|
AddDevice<Device::USB_KBD>("/dev/usb/kbd");
|
||||||
AddDevice<Device::USB_VEN>("/dev/usb/ven");
|
|
||||||
AddDevice<Device::SDIOSlot0>("/dev/sdio/slot0");
|
AddDevice<Device::SDIOSlot0>("/dev/sdio/slot0");
|
||||||
AddDevice<Device::Stub>("/dev/sdio/slot1");
|
AddDevice<Device::Stub>("/dev/sdio/slot1");
|
||||||
#if defined(__LIBUSB__)
|
|
||||||
AddDevice<Device::USB_HIDv4>("/dev/usb/hid");
|
AddDevice<Device::USB_HIDv4>("/dev/usb/hid");
|
||||||
#else
|
AddDevice<Device::OH0>("/dev/usb/oh0");
|
||||||
AddDevice<Device::Stub>("/dev/usb/hid");
|
|
||||||
#endif
|
|
||||||
AddDevice<Device::Stub>("/dev/usb/oh1");
|
AddDevice<Device::Stub>("/dev/usb/oh1");
|
||||||
|
AddDevice<Device::USB_VEN>("/dev/usb/ven");
|
||||||
AddDevice<Device::WFSSRV>("/dev/usb/wfssrv");
|
AddDevice<Device::WFSSRV>("/dev/usb/wfssrv");
|
||||||
AddDevice<Device::WFSI>("/dev/wfsi");
|
AddDevice<Device::WFSI>("/dev/wfsi");
|
||||||
}
|
}
|
||||||
@ -644,6 +653,10 @@ void DoState(PointerWrap& p)
|
|||||||
s_fdmap[i] = std::make_shared<Device::FileIO>(i, "");
|
s_fdmap[i] = std::make_shared<Device::FileIO>(i, "");
|
||||||
s_fdmap[i]->DoState(p);
|
s_fdmap[i]->DoState(p);
|
||||||
break;
|
break;
|
||||||
|
case Device::Device::DeviceType::OH0:
|
||||||
|
s_fdmap[i] = std::make_shared<Device::OH0Device>(i, "");
|
||||||
|
s_fdmap[i]->DoState(p);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -710,6 +723,10 @@ static s32 OpenDevice(const OpenRequest& request)
|
|||||||
if (!device)
|
if (!device)
|
||||||
return IPC_EESEXHAUSTED;
|
return IPC_EESEXHAUSTED;
|
||||||
}
|
}
|
||||||
|
else if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
|
||||||
|
{
|
||||||
|
device = std::make_shared<Device::OH0Device>(new_fd, request.path);
|
||||||
|
}
|
||||||
else if (request.path.find("/dev/") == 0)
|
else if (request.path.find("/dev/") == 0)
|
||||||
{
|
{
|
||||||
device = GetDeviceByName(request.path);
|
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 HLE
|
||||||
} // namespace IOS
|
} // namespace IOS
|
||||||
|
@ -51,6 +51,8 @@ void Init();
|
|||||||
// Needs to be called after Reset(true) to recreate the device tree
|
// Needs to be called after Reset(true) to recreate the device tree
|
||||||
void Reinit();
|
void Reinit();
|
||||||
|
|
||||||
|
u32 GetVersion();
|
||||||
|
|
||||||
bool SetupMemory(u64 ios_title_id);
|
bool SetupMemory(u64 ios_title_id);
|
||||||
|
|
||||||
// Shutdown
|
// Shutdown
|
||||||
@ -77,6 +79,8 @@ void Update();
|
|||||||
// Update Devices
|
// Update Devices
|
||||||
void UpdateDevices();
|
void UpdateDevices();
|
||||||
|
|
||||||
|
void UpdateWantDeterminism(bool new_want_determinism);
|
||||||
|
|
||||||
void ExecuteCommand(u32 address);
|
void ExecuteCommand(u32 address);
|
||||||
|
|
||||||
void EnqueueRequest(u32 address);
|
void EnqueueRequest(u32 address);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/CommonFuncs.h"
|
#include "Common/CommonFuncs.h"
|
||||||
|
#include "Common/LibusbContext.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/Network.h"
|
#include "Common/Network.h"
|
||||||
@ -59,8 +60,8 @@ namespace Device
|
|||||||
BluetoothReal::BluetoothReal(u32 device_id, const std::string& device_name)
|
BluetoothReal::BluetoothReal(u32 device_id, const std::string& device_name)
|
||||||
: BluetoothBase(device_id, device_name)
|
: BluetoothBase(device_id, device_name)
|
||||||
{
|
{
|
||||||
const int ret = libusb_init(&m_libusb_context);
|
m_libusb_context = LibusbContext::Get();
|
||||||
_assert_msg_(IOS_WIIMOTE, ret == 0, "Failed to init libusb.");
|
_assert_msg_(IOS_WIIMOTE, m_libusb_context, "Failed to init libusb.");
|
||||||
|
|
||||||
LoadLinkKeys();
|
LoadLinkKeys();
|
||||||
}
|
}
|
||||||
@ -78,15 +79,13 @@ BluetoothReal::~BluetoothReal()
|
|||||||
libusb_unref_device(m_device);
|
libusb_unref_device(m_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
libusb_exit(m_libusb_context);
|
|
||||||
|
|
||||||
SaveLinkKeys();
|
SaveLinkKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnCode BluetoothReal::Open(const OpenRequest& request)
|
ReturnCode BluetoothReal::Open(const OpenRequest& request)
|
||||||
{
|
{
|
||||||
libusb_device** list;
|
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");
|
_dbg_assert_msg_(IOS, cnt > 0, "Couldn't get device list");
|
||||||
for (ssize_t i = 0; i < cnt; ++i)
|
for (ssize_t i = 0; i < cnt; ++i)
|
||||||
{
|
{
|
||||||
@ -601,7 +600,7 @@ void BluetoothReal::TransferThread()
|
|||||||
Common::SetCurrentThreadName("BT USB Thread");
|
Common::SetCurrentThreadName("BT USB Thread");
|
||||||
while (m_thread_running.IsSet())
|
while (m_thread_running.IsSet())
|
||||||
{
|
{
|
||||||
libusb_handle_events_completed(m_libusb_context, nullptr);
|
libusb_handle_events_completed(m_libusb_context.get(), nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ private:
|
|||||||
|
|
||||||
libusb_device* m_device = nullptr;
|
libusb_device* m_device = nullptr;
|
||||||
libusb_device_handle* m_handle = nullptr;
|
libusb_device_handle* m_handle = nullptr;
|
||||||
libusb_context* m_libusb_context = nullptr;
|
std::shared_ptr<libusb_context> m_libusb_context;
|
||||||
|
|
||||||
Common::Flag m_thread_running;
|
Common::Flag m_thread_running;
|
||||||
std::thread m_thread;
|
std::thread m_thread;
|
||||||
|
@ -2,10 +2,15 @@
|
|||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "Core/IOS/USB/Common.h"
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "Common/Align.h"
|
||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
|
#include "Common/CommonFuncs.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
|
||||||
namespace IOS
|
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");
|
_assert_msg_(IOS_USB, size == 0 || data_address != 0, "Invalid data_address");
|
||||||
Memory::CopyToEmu(data_address, src, size);
|
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<u32>(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<u8>* buffer, const void* data, const size_t size)
|
||||||
|
{
|
||||||
|
buffer->insert(buffer->end(), static_cast<const u8*>(data), static_cast<const u8*>(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<u8>* 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<u8>* buffer, ConfigDescriptor descriptor)
|
||||||
|
{
|
||||||
|
descriptor.wTotalLength = Common::swap16(descriptor.wTotalLength);
|
||||||
|
CopyToBufferAligned(buffer, &descriptor, descriptor.bLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CopyDescriptorToBuffer(std::vector<u8>* buffer, InterfaceDescriptor descriptor)
|
||||||
|
{
|
||||||
|
CopyToBufferAligned(buffer, &descriptor, descriptor.bLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CopyDescriptorToBuffer(std::vector<u8>* 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<u8> Device::GetDescriptorsUSBV4() const
|
||||||
|
{
|
||||||
|
return GetDescriptors([](const auto& descriptor) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> 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<u8>
|
||||||
|
Device::GetDescriptors(std::function<bool(const InterfaceDescriptor&)> predicate) const
|
||||||
|
{
|
||||||
|
std::vector<u8> 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<u8>(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<u8>(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 USB
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace IOS
|
} // namespace IOS
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
@ -17,6 +19,29 @@ namespace HLE
|
|||||||
{
|
{
|
||||||
namespace USB
|
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<u16>(((dir << 7 | type << 5 | recipient) << 8) | request);
|
||||||
|
}
|
||||||
|
|
||||||
struct DeviceDescriptor
|
struct DeviceDescriptor
|
||||||
{
|
{
|
||||||
u8 bLength;
|
u8 bLength;
|
||||||
@ -109,6 +134,49 @@ struct IntrMessage : TransferCommand
|
|||||||
u8 endpoint = 0;
|
u8 endpoint = 0;
|
||||||
using TransferCommand::TransferCommand;
|
using TransferCommand::TransferCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IsoMessage : TransferCommand
|
||||||
|
{
|
||||||
|
u32 packet_sizes_addr = 0;
|
||||||
|
std::vector<u16> 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<u8> GetDescriptorsUSBV4() const;
|
||||||
|
std::vector<u8> GetDescriptorsUSBV5(u8 interface, u8 alt_setting) const;
|
||||||
|
|
||||||
|
virtual DeviceDescriptor GetDeviceDescriptor() const = 0;
|
||||||
|
virtual std::vector<ConfigDescriptor> GetConfigurations() const = 0;
|
||||||
|
virtual std::vector<InterfaceDescriptor> GetInterfaces(u8 config) const = 0;
|
||||||
|
virtual std::vector<EndpointDescriptor> 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<CtrlMessage> message) = 0;
|
||||||
|
virtual int SubmitTransfer(std::unique_ptr<BulkMessage> message) = 0;
|
||||||
|
virtual int SubmitTransfer(std::unique_ptr<IntrMessage> message) = 0;
|
||||||
|
virtual int SubmitTransfer(std::unique_ptr<IsoMessage> message) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<u8> GetDescriptors(std::function<bool(const InterfaceDescriptor&)> predicate) const;
|
||||||
|
u64 m_id = 0xFFFFFFFFFFFFFFFF;
|
||||||
|
};
|
||||||
} // namespace USB
|
} // namespace USB
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace IOS
|
} // namespace IOS
|
||||||
|
244
Source/Core/Core/IOS/USB/Host.cpp
Normal file
244
Source/Core/Core/IOS/USB/Host.cpp
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
#include <libusb.h>
|
||||||
|
#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<USB::Device> device)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> 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<USB::Device> USBHost::GetDeviceById(const u64 device_id) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> 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<USB::Device> 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<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)
|
||||||
|
{
|
||||||
|
#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<int>(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<USB::LibusbDevice>(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<u64>& plugged_devices, DeviceChangeHooks& hooks)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> 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<u64>(), hooks);
|
||||||
|
DispatchHooks(hooks);
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
if (m_event_thread_running.TestAndClear())
|
||||||
|
m_event_thread.join();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USBHost::HandleTransfer(std::shared_ptr<USB::Device> device, u32 request,
|
||||||
|
std::function<s32()> 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
|
85
Source/Core/Core/IOS/USB/Host.h
Normal file
85
Source/Core/Core/IOS/USB/Host.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Flag.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/IPC.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
|
||||||
|
class PointerWrap;
|
||||||
|
struct libusb_context;
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
// Common base class for USB host devices (such as /dev/usb/oh0 and /dev/usb/ven).
|
||||||
|
class USBHost : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
USBHost(u32 device_id, const std::string& device_name);
|
||||||
|
virtual ~USBHost() = default;
|
||||||
|
|
||||||
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
|
|
||||||
|
void UpdateWantDeterminism(bool new_want_determinism) override;
|
||||||
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
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;
|
||||||
|
|
||||||
|
std::shared_ptr<USB::Device> GetDeviceById(u64 device_id) const;
|
||||||
|
virtual void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> changed_device);
|
||||||
|
virtual void OnDeviceChangeEnd();
|
||||||
|
virtual bool ShouldAddDevice(const USB::Device& device) const;
|
||||||
|
void StartThreads();
|
||||||
|
void StopThreads();
|
||||||
|
|
||||||
|
IPCCommandResult HandleTransfer(std::shared_ptr<USB::Device> device, u32 request,
|
||||||
|
std::function<s32()> submit) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool AddDevice(std::unique_ptr<USB::Device> device);
|
||||||
|
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);
|
||||||
|
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
std::shared_ptr<libusb_context> m_libusb_context;
|
||||||
|
// Event thread for libusb
|
||||||
|
Common::Flag m_event_thread_running;
|
||||||
|
std::thread m_event_thread;
|
||||||
|
#endif
|
||||||
|
// Device scanning thread
|
||||||
|
Common::Flag m_scan_thread_running;
|
||||||
|
std::thread m_scan_thread;
|
||||||
|
};
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
437
Source/Core/Core/IOS/USB/LibusbDevice.cpp
Normal file
437
Source/Core/Core/IOS/USB/LibusbDevice.cpp
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
|
||||||
|
#include "Common/Assert.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/IPC.h"
|
||||||
|
#include "Core/IOS/USB/LibusbDevice.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace USB
|
||||||
|
{
|
||||||
|
LibusbDevice::LibusbDevice(libusb_device* device, const libusb_device_descriptor& descriptor)
|
||||||
|
: m_device(device)
|
||||||
|
{
|
||||||
|
libusb_ref_device(m_device);
|
||||||
|
m_vid = descriptor.idVendor;
|
||||||
|
m_pid = descriptor.idProduct;
|
||||||
|
m_id = (static_cast<u64>(m_vid) << 32 | static_cast<u64>(m_pid) << 16 |
|
||||||
|
static_cast<u64>(libusb_get_bus_number(device)) << 8 |
|
||||||
|
static_cast<u64>(libusb_get_device_address(device)));
|
||||||
|
|
||||||
|
for (u8 i = 0; i < descriptor.bNumConfigurations; ++i)
|
||||||
|
m_config_descriptors.emplace_back(std::make_unique<LibusbConfigDescriptor>(m_device, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
LibusbDevice::~LibusbDevice()
|
||||||
|
{
|
||||||
|
if (m_device_attached)
|
||||||
|
DetachInterface();
|
||||||
|
if (m_handle != nullptr)
|
||||||
|
libusb_close(m_handle);
|
||||||
|
libusb_unref_device(m_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceDescriptor LibusbDevice::GetDeviceDescriptor() const
|
||||||
|
{
|
||||||
|
libusb_device_descriptor device_descriptor;
|
||||||
|
libusb_get_device_descriptor(m_device, &device_descriptor);
|
||||||
|
DeviceDescriptor descriptor;
|
||||||
|
// The libusb_device_descriptor struct is the same as the IOS one, and it's not going to change.
|
||||||
|
std::memcpy(&descriptor, &device_descriptor, sizeof(descriptor));
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ConfigDescriptor> LibusbDevice::GetConfigurations() const
|
||||||
|
{
|
||||||
|
std::vector<ConfigDescriptor> descriptors;
|
||||||
|
for (const auto& config_descriptor : m_config_descriptors)
|
||||||
|
{
|
||||||
|
if (!config_descriptor->IsValid())
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "Ignoring invalid config descriptor for %04x:%04x", m_vid, m_pid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ConfigDescriptor descriptor;
|
||||||
|
std::memcpy(&descriptor, config_descriptor->Get(), sizeof(descriptor));
|
||||||
|
descriptors.push_back(descriptor);
|
||||||
|
}
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<InterfaceDescriptor> LibusbDevice::GetInterfaces(const u8 config) const
|
||||||
|
{
|
||||||
|
std::vector<InterfaceDescriptor> descriptors;
|
||||||
|
if (config >= m_config_descriptors.size() || !m_config_descriptors[config]->IsValid())
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "Invalid config descriptor %u for %04x:%04x", config, m_vid, m_pid);
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
for (u8 i = 0; i < m_config_descriptors[config]->Get()->bNumInterfaces; ++i)
|
||||||
|
{
|
||||||
|
const libusb_interface& interface = m_config_descriptors[config]->Get()->interface[i];
|
||||||
|
for (u8 a = 0; a < interface.num_altsetting; ++a)
|
||||||
|
{
|
||||||
|
InterfaceDescriptor descriptor;
|
||||||
|
std::memcpy(&descriptor, &interface.altsetting[a], sizeof(descriptor));
|
||||||
|
descriptors.push_back(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EndpointDescriptor>
|
||||||
|
LibusbDevice::GetEndpoints(const u8 config, const u8 interface_number, const u8 alt_setting) const
|
||||||
|
{
|
||||||
|
std::vector<EndpointDescriptor> descriptors;
|
||||||
|
if (config >= m_config_descriptors.size() || !m_config_descriptors[config]->IsValid())
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "Invalid config descriptor %u for %04x:%04x", config, m_vid, m_pid);
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
_assert_(interface_number < m_config_descriptors[config]->Get()->bNumInterfaces);
|
||||||
|
const auto& interface = m_config_descriptors[config]->Get()->interface[interface_number];
|
||||||
|
_assert_(alt_setting < interface.num_altsetting);
|
||||||
|
const libusb_interface_descriptor& interface_descriptor = interface.altsetting[alt_setting];
|
||||||
|
for (u8 i = 0; i < interface_descriptor.bNumEndpoints; ++i)
|
||||||
|
{
|
||||||
|
EndpointDescriptor descriptor;
|
||||||
|
std::memcpy(&descriptor, &interface_descriptor.endpoint[i], sizeof(descriptor));
|
||||||
|
descriptors.push_back(descriptor);
|
||||||
|
}
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LibusbDevice::GetErrorName(const int error_code) const
|
||||||
|
{
|
||||||
|
return libusb_error_name(error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LibusbDevice::Attach(const u8 interface)
|
||||||
|
{
|
||||||
|
if (m_device_attached && interface != m_active_interface)
|
||||||
|
return ChangeInterface(interface) == 0;
|
||||||
|
|
||||||
|
if (m_device_attached)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_device_attached = false;
|
||||||
|
NOTICE_LOG(IOS_USB, "[%04x:%04x] Opening device", m_vid, m_pid);
|
||||||
|
const int ret = libusb_open(m_device, &m_handle);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to open: %s", m_vid, m_pid, libusb_error_name(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (AttachInterface(interface) != 0)
|
||||||
|
return false;
|
||||||
|
m_device_attached = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::CancelTransfer(const u8 endpoint)
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_USB, "[%04x:%04x %d] Cancelling transfers (endpoint 0x%x)", m_vid, m_pid,
|
||||||
|
m_active_interface, endpoint);
|
||||||
|
const auto iterator = m_transfer_endpoints.find(endpoint);
|
||||||
|
if (iterator == m_transfer_endpoints.cend())
|
||||||
|
return IPC_ENOENT;
|
||||||
|
iterator->second.CancelTransfers();
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::ChangeInterface(const u8 interface)
|
||||||
|
{
|
||||||
|
if (!m_device_attached || interface >= m_config_descriptors[0]->Get()->bNumInterfaces)
|
||||||
|
return LIBUSB_ERROR_NOT_FOUND;
|
||||||
|
|
||||||
|
INFO_LOG(IOS_USB, "[%04x:%04x %d] Changing interface to %d", m_vid, m_pid, m_active_interface,
|
||||||
|
interface);
|
||||||
|
const int ret = DetachInterface();
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
return AttachInterface(interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::SetAltSetting(const u8 alt_setting)
|
||||||
|
{
|
||||||
|
if (!m_device_attached)
|
||||||
|
return LIBUSB_ERROR_NOT_FOUND;
|
||||||
|
|
||||||
|
INFO_LOG(IOS_USB, "[%04x:%04x %d] Setting alt setting %d", m_vid, m_pid, m_active_interface,
|
||||||
|
alt_setting);
|
||||||
|
return libusb_set_interface_alt_setting(m_handle, m_active_interface, alt_setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::SubmitTransfer(std::unique_ptr<CtrlMessage> cmd)
|
||||||
|
{
|
||||||
|
if (!m_device_attached)
|
||||||
|
return LIBUSB_ERROR_NOT_FOUND;
|
||||||
|
|
||||||
|
switch ((cmd->request_type << 8) | cmd->request)
|
||||||
|
{
|
||||||
|
// The following requests have to go through libusb and cannot be directly sent to the device.
|
||||||
|
case USBHDR(DIR_HOST2DEVICE, TYPE_STANDARD, REC_INTERFACE, REQUEST_SET_INTERFACE):
|
||||||
|
{
|
||||||
|
if (static_cast<u8>(cmd->index) != m_active_interface)
|
||||||
|
{
|
||||||
|
const int ret = ChangeInterface(static_cast<u8>(cmd->index));
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x %d] Failed to change interface to %d: %s", m_vid, m_pid,
|
||||||
|
m_active_interface, cmd->index, libusb_error_name(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const int ret = SetAltSetting(static_cast<u8>(cmd->value));
|
||||||
|
if (ret == 0)
|
||||||
|
EnqueueReply(cmd->ios_request, cmd->length);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
case USBHDR(DIR_HOST2DEVICE, TYPE_STANDARD, REC_DEVICE, REQUEST_SET_CONFIGURATION):
|
||||||
|
{
|
||||||
|
const int ret = libusb_set_configuration(m_handle, cmd->value);
|
||||||
|
if (ret == 0)
|
||||||
|
EnqueueReply(cmd->ios_request, cmd->length);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t size = cmd->length + LIBUSB_CONTROL_SETUP_SIZE;
|
||||||
|
auto buffer = std::make_unique<u8[]>(size);
|
||||||
|
libusb_fill_control_setup(buffer.get(), cmd->request_type, cmd->request, cmd->value, cmd->index,
|
||||||
|
cmd->length);
|
||||||
|
Memory::CopyFromEmu(buffer.get() + LIBUSB_CONTROL_SETUP_SIZE, cmd->data_address, cmd->length);
|
||||||
|
libusb_transfer* transfer = libusb_alloc_transfer(0);
|
||||||
|
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||||
|
libusb_fill_control_transfer(transfer, m_handle, buffer.release(), CtrlTransferCallback, this, 0);
|
||||||
|
m_transfer_endpoints[0].AddTransfer(std::move(cmd), transfer);
|
||||||
|
return libusb_submit_transfer(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::SubmitTransfer(std::unique_ptr<BulkMessage> cmd)
|
||||||
|
{
|
||||||
|
if (!m_device_attached)
|
||||||
|
return LIBUSB_ERROR_NOT_FOUND;
|
||||||
|
|
||||||
|
libusb_transfer* transfer = libusb_alloc_transfer(0);
|
||||||
|
libusb_fill_bulk_transfer(transfer, m_handle, cmd->endpoint,
|
||||||
|
cmd->MakeBuffer(cmd->length).release(), cmd->length, TransferCallback,
|
||||||
|
this, 0);
|
||||||
|
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||||
|
m_transfer_endpoints[transfer->endpoint].AddTransfer(std::move(cmd), transfer);
|
||||||
|
return libusb_submit_transfer(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::SubmitTransfer(std::unique_ptr<IntrMessage> cmd)
|
||||||
|
{
|
||||||
|
if (!m_device_attached)
|
||||||
|
return LIBUSB_ERROR_NOT_FOUND;
|
||||||
|
|
||||||
|
libusb_transfer* transfer = libusb_alloc_transfer(0);
|
||||||
|
libusb_fill_interrupt_transfer(transfer, m_handle, cmd->endpoint,
|
||||||
|
cmd->MakeBuffer(cmd->length).release(), cmd->length,
|
||||||
|
TransferCallback, this, 0);
|
||||||
|
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||||
|
m_transfer_endpoints[transfer->endpoint].AddTransfer(std::move(cmd), transfer);
|
||||||
|
return libusb_submit_transfer(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::SubmitTransfer(std::unique_ptr<IsoMessage> cmd)
|
||||||
|
{
|
||||||
|
if (!m_device_attached)
|
||||||
|
return LIBUSB_ERROR_NOT_FOUND;
|
||||||
|
|
||||||
|
libusb_transfer* transfer = libusb_alloc_transfer(cmd->num_packets);
|
||||||
|
transfer->buffer = cmd->MakeBuffer(cmd->length).release();
|
||||||
|
transfer->callback = TransferCallback;
|
||||||
|
transfer->dev_handle = m_handle;
|
||||||
|
transfer->endpoint = cmd->endpoint;
|
||||||
|
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||||
|
for (size_t i = 0; i < cmd->num_packets; ++i)
|
||||||
|
transfer->iso_packet_desc[i].length = cmd->packet_sizes[i];
|
||||||
|
transfer->length = cmd->length;
|
||||||
|
transfer->num_iso_packets = cmd->num_packets;
|
||||||
|
transfer->timeout = 0;
|
||||||
|
transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
|
||||||
|
transfer->user_data = this;
|
||||||
|
m_transfer_endpoints[transfer->endpoint].AddTransfer(std::move(cmd), transfer);
|
||||||
|
return libusb_submit_transfer(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibusbDevice::CtrlTransferCallback(libusb_transfer* transfer)
|
||||||
|
{
|
||||||
|
auto* device = static_cast<LibusbDevice*>(transfer->user_data);
|
||||||
|
device->m_transfer_endpoints[0].HandleTransfer(transfer, [&](const auto& cmd) {
|
||||||
|
cmd.FillBuffer(libusb_control_transfer_get_data(transfer), transfer->actual_length);
|
||||||
|
// The return code is the total transfer length -- *including* the setup packet.
|
||||||
|
return transfer->length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibusbDevice::TransferCallback(libusb_transfer* transfer)
|
||||||
|
{
|
||||||
|
auto* device = static_cast<LibusbDevice*>(transfer->user_data);
|
||||||
|
device->m_transfer_endpoints[transfer->endpoint].HandleTransfer(transfer, [&](const auto& cmd) {
|
||||||
|
switch (transfer->type)
|
||||||
|
{
|
||||||
|
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
|
||||||
|
{
|
||||||
|
auto& iso_msg = static_cast<const IsoMessage&>(cmd);
|
||||||
|
cmd.FillBuffer(transfer->buffer, iso_msg.length);
|
||||||
|
for (size_t i = 0; i < iso_msg.num_packets; ++i)
|
||||||
|
iso_msg.SetPacketReturnValue(i, transfer->iso_packet_desc[i].actual_length);
|
||||||
|
// Note: isochronous transfers *must* return 0 as the return value. Anything else
|
||||||
|
// (such as the number of bytes transferred) is considered as a failure.
|
||||||
|
return static_cast<s32>(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
cmd.FillBuffer(transfer->buffer, transfer->actual_length);
|
||||||
|
return static_cast<s32>(transfer->actual_length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::map<u8, const char*> s_transfer_types = {
|
||||||
|
{LIBUSB_TRANSFER_TYPE_CONTROL, "Control"},
|
||||||
|
{LIBUSB_TRANSFER_TYPE_ISOCHRONOUS, "Isochronous"},
|
||||||
|
{LIBUSB_TRANSFER_TYPE_BULK, "Bulk"},
|
||||||
|
{LIBUSB_TRANSFER_TYPE_INTERRUPT, "Interrupt"},
|
||||||
|
};
|
||||||
|
|
||||||
|
void LibusbDevice::TransferEndpoint::AddTransfer(std::unique_ptr<TransferCommand> command,
|
||||||
|
libusb_transfer* transfer)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_transfers_mutex};
|
||||||
|
m_transfers.emplace(transfer, std::move(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibusbDevice::TransferEndpoint::HandleTransfer(libusb_transfer* transfer,
|
||||||
|
std::function<s32(const TransferCommand&)> fn)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_transfers_mutex};
|
||||||
|
const auto iterator = m_transfers.find(transfer);
|
||||||
|
if (iterator == m_transfers.cend())
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "No such transfer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unique_ptr<u8[]> buffer(transfer->buffer);
|
||||||
|
const auto& cmd = *iterator->second.get();
|
||||||
|
const auto* device = static_cast<LibusbDevice*>(transfer->user_data);
|
||||||
|
s32 return_value = 0;
|
||||||
|
switch (transfer->status)
|
||||||
|
{
|
||||||
|
case LIBUSB_TRANSFER_COMPLETED:
|
||||||
|
return_value = fn(cmd);
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_ERROR:
|
||||||
|
case LIBUSB_TRANSFER_CANCELLED:
|
||||||
|
case LIBUSB_TRANSFER_TIMED_OUT:
|
||||||
|
case LIBUSB_TRANSFER_OVERFLOW:
|
||||||
|
case LIBUSB_TRANSFER_STALL:
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x %d] %s transfer (endpoint 0x%02x) failed: %s", device->m_vid,
|
||||||
|
device->m_pid, device->m_active_interface, s_transfer_types.at(transfer->type),
|
||||||
|
transfer->endpoint, libusb_error_name(transfer->status));
|
||||||
|
return_value = transfer->status == LIBUSB_TRANSFER_STALL ? -7004 : -5;
|
||||||
|
break;
|
||||||
|
case LIBUSB_TRANSFER_NO_DEVICE:
|
||||||
|
return_value = IPC_ENOENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cmd.OnTransferComplete();
|
||||||
|
EnqueueReply(cmd.ios_request, return_value, 0, CoreTiming::FromThread::NON_CPU);
|
||||||
|
m_transfers.erase(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibusbDevice::TransferEndpoint::CancelTransfers()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_transfers_mutex);
|
||||||
|
if (m_transfers.empty())
|
||||||
|
return;
|
||||||
|
INFO_LOG(IOS_USB, "Cancelling %ld transfer(s)", m_transfers.size());
|
||||||
|
for (const auto& pending_transfer : m_transfers)
|
||||||
|
libusb_cancel_transfer(pending_transfer.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::GetNumberOfAltSettings(const u8 interface_number)
|
||||||
|
{
|
||||||
|
return m_config_descriptors[0]->Get()->interface[interface_number].num_altsetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::AttachInterface(const u8 interface)
|
||||||
|
{
|
||||||
|
if (m_handle == nullptr)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x] Cannot attach without a valid device handle", m_vid, m_pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_LOG(IOS_USB, "[%04x:%04x] Attaching interface %d", m_vid, m_pid, interface);
|
||||||
|
const int ret = libusb_detach_kernel_driver(m_handle, interface);
|
||||||
|
if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to detach kernel driver: %s", m_vid, m_pid,
|
||||||
|
libusb_error_name(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
const int r = libusb_claim_interface(m_handle, interface);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x] Couldn't claim interface %d: %s", m_vid, m_pid, interface,
|
||||||
|
libusb_error_name(r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
m_active_interface = interface;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LibusbDevice::DetachInterface()
|
||||||
|
{
|
||||||
|
if (m_handle == nullptr)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x] Cannot detach without a valid device handle", m_vid, m_pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_LOG(IOS_USB, "[%04x:%04x] Detaching interface %d", m_vid, m_pid, m_active_interface);
|
||||||
|
const int ret = libusb_release_interface(m_handle, m_active_interface);
|
||||||
|
if (ret < 0 && ret != LIBUSB_ERROR_NO_DEVICE)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to release interface %d: %s", m_vid, m_pid,
|
||||||
|
m_active_interface, libusb_error_name(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LibusbConfigDescriptor::LibusbConfigDescriptor(libusb_device* device, const u8 config_num)
|
||||||
|
{
|
||||||
|
if (libusb_get_config_descriptor(device, config_num, &m_descriptor) != LIBUSB_SUCCESS)
|
||||||
|
m_descriptor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
LibusbConfigDescriptor::~LibusbConfigDescriptor()
|
||||||
|
{
|
||||||
|
if (m_descriptor != nullptr)
|
||||||
|
libusb_free_config_descriptor(m_descriptor);
|
||||||
|
}
|
||||||
|
} // namespace USB
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
94
Source/Core/Core/IOS/USB/LibusbDevice.h
Normal file
94
Source/Core/Core/IOS/USB/LibusbDevice.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__LIBUSB__)
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
|
||||||
|
struct libusb_config_descriptor;
|
||||||
|
struct libusb_device;
|
||||||
|
struct libusb_device_descriptor;
|
||||||
|
struct libusb_device_handle;
|
||||||
|
struct libusb_transfer;
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace USB
|
||||||
|
{
|
||||||
|
// Simple wrapper around libusb_get_config_descriptor and libusb_free_config_descriptor.
|
||||||
|
class LibusbConfigDescriptor final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LibusbConfigDescriptor(libusb_device* device, u8 config_num = 0);
|
||||||
|
~LibusbConfigDescriptor();
|
||||||
|
libusb_config_descriptor* Get() const { return m_descriptor; }
|
||||||
|
bool IsValid() const { return m_descriptor != nullptr; }
|
||||||
|
private:
|
||||||
|
libusb_config_descriptor* m_descriptor = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LibusbDevice final : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LibusbDevice(libusb_device* device, const libusb_device_descriptor& device_descriptor);
|
||||||
|
~LibusbDevice();
|
||||||
|
DeviceDescriptor GetDeviceDescriptor() const override;
|
||||||
|
std::vector<ConfigDescriptor> GetConfigurations() const override;
|
||||||
|
std::vector<InterfaceDescriptor> GetInterfaces(u8 config) const override;
|
||||||
|
std::vector<EndpointDescriptor> GetEndpoints(u8 config, u8 interface, u8 alt) const override;
|
||||||
|
std::string GetErrorName(int error_code) const override;
|
||||||
|
bool Attach(u8 interface) override;
|
||||||
|
int CancelTransfer(u8 endpoint) override;
|
||||||
|
int ChangeInterface(u8 interface) override;
|
||||||
|
int GetNumberOfAltSettings(u8 interface) override;
|
||||||
|
int SetAltSetting(u8 alt_setting) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<CtrlMessage> message) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<BulkMessage> message) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<IntrMessage> message) override;
|
||||||
|
int SubmitTransfer(std::unique_ptr<IsoMessage> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<LibusbConfigDescriptor>> m_config_descriptors;
|
||||||
|
u16 m_vid = 0;
|
||||||
|
u16 m_pid = 0;
|
||||||
|
u8 m_active_interface = 0;
|
||||||
|
bool m_device_attached = false;
|
||||||
|
|
||||||
|
libusb_device* m_device = nullptr;
|
||||||
|
libusb_device_handle* m_handle = nullptr;
|
||||||
|
|
||||||
|
class TransferEndpoint final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void AddTransfer(std::unique_ptr<TransferCommand> command, libusb_transfer* transfer);
|
||||||
|
void HandleTransfer(libusb_transfer* tr, std::function<s32(const TransferCommand&)> function);
|
||||||
|
void CancelTransfers();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex m_transfers_mutex;
|
||||||
|
std::map<libusb_transfer*, std::unique_ptr<TransferCommand>> m_transfers;
|
||||||
|
};
|
||||||
|
std::map<u8, TransferEndpoint> m_transfer_endpoints;
|
||||||
|
static void CtrlTransferCallback(libusb_transfer* transfer);
|
||||||
|
static void TransferCallback(libusb_transfer* transfer);
|
||||||
|
|
||||||
|
int AttachInterface(u8 interface);
|
||||||
|
int DetachInterface();
|
||||||
|
};
|
||||||
|
} // namespace USB
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
||||||
|
#endif
|
356
Source/Core/Core/IOS/USB/OH0/OH0.cpp
Normal file
356
Source/Core/Core/IOS/USB/OH0/OH0.cpp
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <istream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "Common/Assert.h"
|
||||||
|
#include "Common/ChunkFile.h"
|
||||||
|
#include "Common/CommonFuncs.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Core/Core.h"
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
#include "Core/IOS/USB/OH0/OH0.h"
|
||||||
|
#include "Core/IOS/USB/USBV0.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
OH0::OH0(u32 device_id, const std::string& device_name) : USBHost(device_id, device_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OH0::~OH0()
|
||||||
|
{
|
||||||
|
StopThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode OH0::Open(const OpenRequest& request)
|
||||||
|
{
|
||||||
|
const u32 ios_major_version = GetVersion();
|
||||||
|
if (ios_major_version == 57 || ios_major_version == 58 || ios_major_version == 59)
|
||||||
|
return IPC_EACCES;
|
||||||
|
return USBHost::Open(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::IOCtl(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
request.Log(GetDeviceName(), LogTypes::IOS_USB);
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTL_USBV0_GETRHDESCA:
|
||||||
|
return GetRhDesca(request);
|
||||||
|
case USB::IOCTL_USBV0_CANCEL_INSERT_HOOK:
|
||||||
|
return CancelInsertionHook(request);
|
||||||
|
default:
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::IOCtlV(const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_USB, "/dev/usb/oh0 - IOCtlV %u", request.request);
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTLV_USBV0_GETDEVLIST:
|
||||||
|
return GetDeviceList(request);
|
||||||
|
case USB::IOCTLV_USBV0_GETRHPORTSTATUS:
|
||||||
|
return GetRhPortStatus(request);
|
||||||
|
case USB::IOCTLV_USBV0_SETRHPORTSTATUS:
|
||||||
|
return SetRhPortStatus(request);
|
||||||
|
case USB::IOCTLV_USBV0_DEVINSERTHOOK:
|
||||||
|
return RegisterInsertionHook(request);
|
||||||
|
case USB::IOCTLV_USBV0_DEVICECLASSCHANGE:
|
||||||
|
return RegisterClassChangeHook(request);
|
||||||
|
case USB::IOCTLV_USBV0_DEVINSERTHOOKID:
|
||||||
|
return RegisterInsertionHookWithID(request);
|
||||||
|
default:
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OH0::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||||
|
{
|
||||||
|
Core::DisplayMessage("It is suggested that you unplug and replug all connected USB devices.",
|
||||||
|
5000);
|
||||||
|
Core::DisplayMessage("If USB doesn't work properly, an emulation reset may be needed.", 5000);
|
||||||
|
}
|
||||||
|
p.Do(m_insertion_hooks);
|
||||||
|
p.Do(m_removal_hooks);
|
||||||
|
p.Do(m_opened_devices);
|
||||||
|
USBHost::DoState(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::CancelInsertionHook(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.buffer_in || request.buffer_in_size != 4)
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
// IOS assigns random IDs, but ours are simply the VID + PID (see RegisterInsertionHookWithID)
|
||||||
|
TriggerHook(m_insertion_hooks,
|
||||||
|
{Memory::Read_U16(request.buffer_in), Memory::Read_U16(request.buffer_in + 2)},
|
||||||
|
USB_ECANCELED);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::GetDeviceList(const IOCtlVRequest& request) const
|
||||||
|
{
|
||||||
|
if (!request.HasNumberOfValidVectors(2, 2))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
const u8 max_entries_count = Memory::Read_U8(request.in_vectors[0].address);
|
||||||
|
if (request.io_vectors[1].size != max_entries_count * sizeof(DeviceEntry))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
const u8 interface_class = Memory::Read_U8(request.in_vectors[1].address);
|
||||||
|
u8 entries_count = 0;
|
||||||
|
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||||
|
for (const auto& device : m_devices)
|
||||||
|
{
|
||||||
|
if (entries_count >= max_entries_count)
|
||||||
|
break;
|
||||||
|
if (!device.second->HasClass(interface_class))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DeviceEntry entry;
|
||||||
|
entry.unknown = 0;
|
||||||
|
entry.vid = Common::swap16(device.second->GetVid());
|
||||||
|
entry.pid = Common::swap16(device.second->GetPid());
|
||||||
|
Memory::CopyToEmu(request.io_vectors[1].address + 8 * entries_count++, &entry, 8);
|
||||||
|
}
|
||||||
|
Memory::Write_U8(entries_count, request.io_vectors[0].address);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::GetRhDesca(const IOCtlRequest& request) const
|
||||||
|
{
|
||||||
|
if (!request.buffer_out || request.buffer_out_size != 4)
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
// Based on a hardware test, this ioctl seems to return a constant value
|
||||||
|
Memory::Write_U32(0x02000302, request.buffer_out);
|
||||||
|
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LWARNING);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::GetRhPortStatus(const IOCtlVRequest& request) const
|
||||||
|
{
|
||||||
|
if (!request.HasNumberOfValidVectors(1, 1))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
ERROR_LOG(IOS_USB, "Unimplemented IOCtlV: IOCTLV_USBV0_GETRHPORTSTATUS");
|
||||||
|
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::SetRhPortStatus(const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.HasNumberOfValidVectors(2, 0))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
ERROR_LOG(IOS_USB, "Unimplemented IOCtlV: IOCTLV_USBV0_SETRHPORTSTATUS");
|
||||||
|
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::RegisterRemovalHook(const u64 device_id, const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{m_hooks_mutex};
|
||||||
|
// IOS only allows a single device removal hook.
|
||||||
|
if (m_removal_hooks.find(device_id) != m_removal_hooks.end())
|
||||||
|
return GetDefaultReply(IPC_EEXIST);
|
||||||
|
m_removal_hooks.insert({device_id, request.address});
|
||||||
|
return GetNoReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::RegisterInsertionHook(const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.HasNumberOfValidVectors(2, 0))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
const u16 vid = Memory::Read_U16(request.in_vectors[0].address);
|
||||||
|
const u16 pid = Memory::Read_U16(request.in_vectors[1].address);
|
||||||
|
if (HasDeviceWithVidPid(vid, pid))
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock{m_hooks_mutex};
|
||||||
|
// TODO: figure out whether IOS allows more than one hook.
|
||||||
|
m_insertion_hooks[{vid, pid}] = request.address;
|
||||||
|
return GetNoReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::RegisterInsertionHookWithID(const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.HasNumberOfValidVectors(3, 1))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock{m_hooks_mutex};
|
||||||
|
const u16 vid = Memory::Read_U16(request.in_vectors[0].address);
|
||||||
|
const u16 pid = Memory::Read_U16(request.in_vectors[1].address);
|
||||||
|
const bool trigger_only_for_new_device = Memory::Read_U8(request.in_vectors[2].address) == 1;
|
||||||
|
if (!trigger_only_for_new_device && HasDeviceWithVidPid(vid, pid))
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
// TODO: figure out whether IOS allows more than one hook.
|
||||||
|
m_insertion_hooks.insert({{vid, pid}, request.address});
|
||||||
|
// The output vector is overwritten with an ID to use with ioctl 31 for cancelling the hook.
|
||||||
|
Memory::Write_U32(vid << 16 | pid, request.io_vectors[0].address);
|
||||||
|
return GetNoReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::RegisterClassChangeHook(const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.HasNumberOfValidVectors(1, 0))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
WARN_LOG(IOS_USB, "Unimplemented IOCtlV: USB::IOCTLV_USBV0_DEVICECLASSCHANGE (no reply)");
|
||||||
|
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LWARNING);
|
||||||
|
return GetNoReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OH0::HasDeviceWithVidPid(const u16 vid, const u16 pid) const
|
||||||
|
{
|
||||||
|
return std::any_of(m_devices.begin(), m_devices.end(), [=](const auto& device) {
|
||||||
|
return device.second->GetVid() == vid && device.second->GetPid() == pid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void OH0::OnDeviceChange(const ChangeEvent event, std::shared_ptr<USB::Device> device)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||||
|
if (event == ChangeEvent::Inserted)
|
||||||
|
TriggerHook(m_insertion_hooks, {device->GetVid(), device->GetPid()}, IPC_SUCCESS);
|
||||||
|
else if (event == ChangeEvent::Removed)
|
||||||
|
TriggerHook(m_removal_hooks, device->GetId(), IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void OH0::TriggerHook(std::map<T, u32>& hooks, T value, const ReturnCode return_value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_hooks_mutex};
|
||||||
|
const auto hook = hooks.find(value);
|
||||||
|
if (hook == hooks.end())
|
||||||
|
return;
|
||||||
|
EnqueueReply(Request{hook->second}, return_value, 0, CoreTiming::FromThread::ANY);
|
||||||
|
hooks.erase(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<ReturnCode, u64> OH0::DeviceOpen(const u16 vid, const u16 pid)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||||
|
|
||||||
|
bool has_device_with_vid_pid = false;
|
||||||
|
for (const auto& device : m_devices)
|
||||||
|
{
|
||||||
|
if (device.second->GetVid() != vid || device.second->GetPid() != pid)
|
||||||
|
continue;
|
||||||
|
has_device_with_vid_pid = true;
|
||||||
|
|
||||||
|
if (m_opened_devices.find(device.second->GetId()) != m_opened_devices.cend() ||
|
||||||
|
!device.second->Attach(0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_opened_devices.emplace(device.second->GetId());
|
||||||
|
return {IPC_SUCCESS, device.second->GetId()};
|
||||||
|
}
|
||||||
|
// IOS doesn't allow opening the same device more than once (IPC_EEXIST)
|
||||||
|
return {has_device_with_vid_pid ? IPC_EEXIST : IPC_ENOENT, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
void OH0::DeviceClose(const u64 device_id)
|
||||||
|
{
|
||||||
|
TriggerHook(m_removal_hooks, device_id, IPC_ENOENT);
|
||||||
|
m_opened_devices.erase(device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::DeviceIOCtl(const u64 device_id, const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
const auto device = GetDeviceById(device_id);
|
||||||
|
if (!device)
|
||||||
|
return GetDefaultReply(IPC_ENOENT);
|
||||||
|
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTL_USBV0_DEVREMOVALHOOK:
|
||||||
|
return RegisterRemovalHook(device_id, request);
|
||||||
|
case USB::IOCTL_USBV0_SUSPENDDEV:
|
||||||
|
case USB::IOCTL_USBV0_RESUMEDEV:
|
||||||
|
// Unimplemented because libusb doesn't do power management.
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
case USB::IOCTL_USBV0_RESET_DEVICE:
|
||||||
|
TriggerHook(m_removal_hooks, device_id, IPC_SUCCESS);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
default:
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0::DeviceIOCtlV(const u64 device_id, const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
const auto device = GetDeviceById(device_id);
|
||||||
|
if (!device)
|
||||||
|
return GetDefaultReply(IPC_ENOENT);
|
||||||
|
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTLV_USBV0_CTRLMSG:
|
||||||
|
case USB::IOCTLV_USBV0_BLKMSG:
|
||||||
|
case USB::IOCTLV_USBV0_LBLKMSG:
|
||||||
|
case USB::IOCTLV_USBV0_INTRMSG:
|
||||||
|
case USB::IOCTLV_USBV0_ISOMSG:
|
||||||
|
return HandleTransfer(device, request.request,
|
||||||
|
[&, this]() { return SubmitTransfer(*device, request); });
|
||||||
|
case USB::IOCTLV_USBV0_UNKNOWN_32:
|
||||||
|
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
default:
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 OH0::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv)
|
||||||
|
{
|
||||||
|
switch (ioctlv.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTLV_USBV0_CTRLMSG:
|
||||||
|
if (!ioctlv.HasNumberOfValidVectors(6, 1) ||
|
||||||
|
Common::swap16(Memory::Read_U16(ioctlv.in_vectors[4].address)) != ioctlv.io_vectors[0].size)
|
||||||
|
return IPC_EINVAL;
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V0CtrlMessage>(ioctlv));
|
||||||
|
|
||||||
|
case USB::IOCTLV_USBV0_BLKMSG:
|
||||||
|
case USB::IOCTLV_USBV0_LBLKMSG:
|
||||||
|
if (!ioctlv.HasNumberOfValidVectors(2, 1) ||
|
||||||
|
Memory::Read_U16(ioctlv.in_vectors[1].address) != ioctlv.io_vectors[0].size)
|
||||||
|
return IPC_EINVAL;
|
||||||
|
return device.SubmitTransfer(
|
||||||
|
std::make_unique<USB::V0BulkMessage>(ioctlv, ioctlv.request == USB::IOCTLV_USBV0_LBLKMSG));
|
||||||
|
|
||||||
|
case USB::IOCTLV_USBV0_INTRMSG:
|
||||||
|
if (!ioctlv.HasNumberOfValidVectors(2, 1) ||
|
||||||
|
Memory::Read_U16(ioctlv.in_vectors[1].address) != ioctlv.io_vectors[0].size)
|
||||||
|
return IPC_EINVAL;
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V0IntrMessage>(ioctlv));
|
||||||
|
|
||||||
|
case USB::IOCTLV_USBV0_ISOMSG:
|
||||||
|
if (!ioctlv.HasNumberOfValidVectors(3, 2))
|
||||||
|
return IPC_EINVAL;
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V0IsoMessage>(ioctlv));
|
||||||
|
|
||||||
|
default:
|
||||||
|
return IPC_EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
88
Source/Core/Core/IOS/USB/OH0/OH0.h
Normal file
88
Source/Core/Core/IOS/USB/OH0/OH0.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/USB/Host.h"
|
||||||
|
|
||||||
|
class PointerWrap;
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace USB
|
||||||
|
{
|
||||||
|
struct DeviceInfo
|
||||||
|
{
|
||||||
|
u16 vid;
|
||||||
|
u16 pid;
|
||||||
|
bool operator<(const DeviceInfo& other) const { return vid < other.vid; }
|
||||||
|
};
|
||||||
|
} // namespace USB
|
||||||
|
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
// /dev/usb/oh0
|
||||||
|
class OH0 final : public USBHost
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OH0(u32 device_id, const std::string& device_name);
|
||||||
|
~OH0() override;
|
||||||
|
|
||||||
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
|
|
||||||
|
std::pair<ReturnCode, u64> DeviceOpen(u16 vid, u16 pid);
|
||||||
|
void DeviceClose(u64 device_id);
|
||||||
|
IPCCommandResult DeviceIOCtl(u64 device_id, const IOCtlRequest& request);
|
||||||
|
IPCCommandResult DeviceIOCtlV(u64 device_id, const IOCtlVRequest& request);
|
||||||
|
|
||||||
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
IPCCommandResult CancelInsertionHook(const IOCtlRequest& request);
|
||||||
|
IPCCommandResult GetDeviceList(const IOCtlVRequest& request) const;
|
||||||
|
IPCCommandResult GetRhDesca(const IOCtlRequest& request) const;
|
||||||
|
IPCCommandResult GetRhPortStatus(const IOCtlVRequest& request) const;
|
||||||
|
IPCCommandResult SetRhPortStatus(const IOCtlVRequest& request);
|
||||||
|
IPCCommandResult RegisterRemovalHook(u64 device_id, const IOCtlRequest& request);
|
||||||
|
IPCCommandResult RegisterInsertionHook(const IOCtlVRequest& request);
|
||||||
|
IPCCommandResult RegisterInsertionHookWithID(const IOCtlVRequest& request);
|
||||||
|
IPCCommandResult RegisterClassChangeHook(const IOCtlVRequest& request);
|
||||||
|
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request);
|
||||||
|
|
||||||
|
bool HasDeviceWithVidPid(u16 vid, u16 pid) const;
|
||||||
|
void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> device) override;
|
||||||
|
template <typename T>
|
||||||
|
void TriggerHook(std::map<T, u32>& hooks, T value, ReturnCode return_value);
|
||||||
|
|
||||||
|
struct DeviceEntry
|
||||||
|
{
|
||||||
|
u32 unknown;
|
||||||
|
u16 vid;
|
||||||
|
u16 pid;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DeviceEntry) == 8, "sizeof(DeviceEntry) must be 8");
|
||||||
|
|
||||||
|
// Device info (VID, PID) → command address for pending hook requests
|
||||||
|
std::map<USB::DeviceInfo, u32> m_insertion_hooks;
|
||||||
|
std::map<u64, u32> m_removal_hooks;
|
||||||
|
std::set<u64> m_opened_devices;
|
||||||
|
std::mutex m_hooks_mutex;
|
||||||
|
};
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
87
Source/Core/Core/IOS/USB/OH0/OH0Device.cpp
Normal file
87
Source/Core/Core/IOS/USB/OH0/OH0Device.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/ChunkFile.h"
|
||||||
|
#include "Core/IOS/IPC.h"
|
||||||
|
#include "Core/IOS/USB/OH0/OH0.h"
|
||||||
|
#include "Core/IOS/USB/OH0/OH0Device.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
static void GetVidPidFromDevicePath(const std::string& device_path, u16& vid, u16& pid)
|
||||||
|
{
|
||||||
|
std::stringstream stream{device_path};
|
||||||
|
std::string segment;
|
||||||
|
std::vector<std::string> list;
|
||||||
|
while (std::getline(stream, segment, '/'))
|
||||||
|
if (!segment.empty())
|
||||||
|
list.push_back(segment);
|
||||||
|
|
||||||
|
if (list.size() != 5)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::hex << list[3];
|
||||||
|
ss >> vid;
|
||||||
|
ss.clear();
|
||||||
|
ss << std::hex << list[4];
|
||||||
|
ss >> pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
OH0Device::OH0Device(u32 id, const std::string& name) : Device(id, name, DeviceType::OH0)
|
||||||
|
{
|
||||||
|
if (!name.empty())
|
||||||
|
GetVidPidFromDevicePath(name, m_vid, m_pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OH0Device::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
m_oh0 = std::static_pointer_cast<OH0>(GetDeviceByName("/dev/usb/oh0"));
|
||||||
|
p.Do(m_name);
|
||||||
|
p.Do(m_vid);
|
||||||
|
p.Do(m_pid);
|
||||||
|
p.Do(m_device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode OH0Device::Open(const OpenRequest& request)
|
||||||
|
{
|
||||||
|
const u32 ios_major_version = GetVersion();
|
||||||
|
if (ios_major_version == 57 || ios_major_version == 58 || ios_major_version == 59)
|
||||||
|
return IPC_ENOENT;
|
||||||
|
|
||||||
|
if (m_vid == 0 && m_pid == 0)
|
||||||
|
return IPC_ENOENT;
|
||||||
|
|
||||||
|
m_oh0 = std::static_pointer_cast<OH0>(GetDeviceByName("/dev/usb/oh0"));
|
||||||
|
|
||||||
|
ReturnCode return_code;
|
||||||
|
std::tie(return_code, m_device_id) = m_oh0->DeviceOpen(m_vid, m_pid);
|
||||||
|
return return_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OH0Device::Close()
|
||||||
|
{
|
||||||
|
m_oh0->DeviceClose(m_device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0Device::IOCtl(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
return m_oh0->DeviceIOCtl(m_device_id, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult OH0Device::IOCtlV(const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
return m_oh0->DeviceIOCtlV(m_device_id, request);
|
||||||
|
}
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
41
Source/Core/Core/IOS/USB/OH0/OH0Device.h
Normal file
41
Source/Core/Core/IOS/USB/OH0/OH0Device.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
|
||||||
|
class PointerWrap;
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
class OH0;
|
||||||
|
class OH0Device final : public Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OH0Device(u32 device_id, const std::string& device_name);
|
||||||
|
|
||||||
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
|
void Close() override;
|
||||||
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<OH0> m_oh0;
|
||||||
|
u16 m_vid = 0;
|
||||||
|
u16 m_pid = 0;
|
||||||
|
u64 m_device_id = 0;
|
||||||
|
};
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
@ -42,6 +42,17 @@ V0IntrMessage::V0IntrMessage(const IOCtlVRequest& ioctlv)
|
|||||||
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address);
|
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address);
|
||||||
length = Memory::Read_U16(ioctlv.in_vectors[1].address);
|
length = Memory::Read_U16(ioctlv.in_vectors[1].address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
V0IsoMessage::V0IsoMessage(const IOCtlVRequest& ioctlv)
|
||||||
|
: IsoMessage(ioctlv, ioctlv.io_vectors[1].address)
|
||||||
|
{
|
||||||
|
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address);
|
||||||
|
length = Memory::Read_U16(ioctlv.in_vectors[1].address);
|
||||||
|
num_packets = Memory::Read_U8(ioctlv.in_vectors[2].address);
|
||||||
|
packet_sizes_addr = ioctlv.io_vectors[0].address;
|
||||||
|
for (size_t i = 0; i < num_packets; ++i)
|
||||||
|
packet_sizes.push_back(Memory::Read_U16(static_cast<u32>(packet_sizes_addr + i * sizeof(u16))));
|
||||||
|
}
|
||||||
} // namespace USB
|
} // namespace USB
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace IOS
|
} // namespace IOS
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Core/IOS/USB/Common.h"
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
|
||||||
@ -24,6 +22,21 @@ enum V0Requests
|
|||||||
IOCTLV_USBV0_CTRLMSG = 0,
|
IOCTLV_USBV0_CTRLMSG = 0,
|
||||||
IOCTLV_USBV0_BLKMSG = 1,
|
IOCTLV_USBV0_BLKMSG = 1,
|
||||||
IOCTLV_USBV0_INTRMSG = 2,
|
IOCTLV_USBV0_INTRMSG = 2,
|
||||||
|
IOCTL_USBV0_SUSPENDDEV = 5,
|
||||||
|
IOCTL_USBV0_RESUMEDEV = 6,
|
||||||
|
IOCTLV_USBV0_ISOMSG = 9,
|
||||||
|
IOCTLV_USBV0_LBLKMSG = 10,
|
||||||
|
IOCTLV_USBV0_GETDEVLIST = 12,
|
||||||
|
IOCTL_USBV0_GETRHDESCA = 15,
|
||||||
|
IOCTLV_USBV0_GETRHPORTSTATUS = 20,
|
||||||
|
IOCTLV_USBV0_SETRHPORTSTATUS = 25,
|
||||||
|
IOCTL_USBV0_DEVREMOVALHOOK = 26,
|
||||||
|
IOCTLV_USBV0_DEVINSERTHOOK = 27,
|
||||||
|
IOCTLV_USBV0_DEVICECLASSCHANGE = 28,
|
||||||
|
IOCTL_USBV0_RESET_DEVICE = 29,
|
||||||
|
IOCTLV_USBV0_DEVINSERTHOOKID = 30,
|
||||||
|
IOCTL_USBV0_CANCEL_INSERT_HOOK = 31,
|
||||||
|
IOCTLV_USBV0_UNKNOWN_32 = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V0CtrlMessage final : CtrlMessage
|
struct V0CtrlMessage final : CtrlMessage
|
||||||
@ -40,6 +53,11 @@ struct V0IntrMessage final : IntrMessage
|
|||||||
{
|
{
|
||||||
explicit V0IntrMessage(const IOCtlVRequest& ioctlv);
|
explicit V0IntrMessage(const IOCtlVRequest& ioctlv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct V0IsoMessage final : IsoMessage
|
||||||
|
{
|
||||||
|
explicit V0IsoMessage(const IOCtlVRequest& ioctlv);
|
||||||
|
};
|
||||||
} // namespace USB
|
} // namespace USB
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace IOS
|
} // namespace IOS
|
||||||
|
97
Source/Core/Core/IOS/USB/USBV4.cpp
Normal file
97
Source/Core/Core/IOS/USB/USBV4.cpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <locale>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "Common/CommonFuncs.h"
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/USB/USBV4.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace USB
|
||||||
|
{
|
||||||
|
// Source: https://wiibrew.org/w/index.php?title=/dev/usb/hid&oldid=96809
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct HIDRequest
|
||||||
|
{
|
||||||
|
u8 padding[16];
|
||||||
|
s32 device_no;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u8 bmRequestType;
|
||||||
|
u8 bmRequest;
|
||||||
|
u16 wValue;
|
||||||
|
u16 wIndex;
|
||||||
|
u16 wLength;
|
||||||
|
} control;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u32 endpoint;
|
||||||
|
u32 length;
|
||||||
|
} interrupt;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u8 bIndex;
|
||||||
|
} string;
|
||||||
|
};
|
||||||
|
u32 data_addr;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
V4CtrlMessage::V4CtrlMessage(const IOCtlRequest& ioctl) : CtrlMessage(ioctl, -1)
|
||||||
|
{
|
||||||
|
HIDRequest hid_request;
|
||||||
|
Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request));
|
||||||
|
request_type = hid_request.control.bmRequestType;
|
||||||
|
request = hid_request.control.bmRequest;
|
||||||
|
value = Common::swap16(hid_request.control.wValue);
|
||||||
|
index = Common::swap16(hid_request.control.wIndex);
|
||||||
|
length = Common::swap16(hid_request.control.wLength);
|
||||||
|
data_address = Common::swap32(hid_request.data_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since this is just a standard control request, but with additional requirements
|
||||||
|
// (US for the language and replacing non-ASCII characters with '?'),
|
||||||
|
// we can simply submit it as a usual control request.
|
||||||
|
V4GetUSStringMessage::V4GetUSStringMessage(const IOCtlRequest& ioctl) : CtrlMessage(ioctl, -1)
|
||||||
|
{
|
||||||
|
HIDRequest hid_request;
|
||||||
|
Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request));
|
||||||
|
request_type = 0x80;
|
||||||
|
request = REQUEST_GET_DESCRIPTOR;
|
||||||
|
value = (0x03 << 8) | hid_request.string.bIndex;
|
||||||
|
index = 0x0409; // language US
|
||||||
|
length = 255;
|
||||||
|
data_address = Common::swap32(hid_request.data_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void V4GetUSStringMessage::OnTransferComplete() const
|
||||||
|
{
|
||||||
|
const std::locale& c_locale = std::locale::classic();
|
||||||
|
std::string message = Memory::GetString(data_address);
|
||||||
|
std::replace_if(message.begin(), message.end(),
|
||||||
|
[&c_locale](char c) { return !std::isprint(c, c_locale); }, '?');
|
||||||
|
Memory::CopyToEmu(data_address, message.c_str(), message.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
V4IntrMessage::V4IntrMessage(const IOCtlRequest& ioctl) : IntrMessage(ioctl, -1)
|
||||||
|
{
|
||||||
|
HIDRequest hid_request;
|
||||||
|
Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request));
|
||||||
|
length = static_cast<u16>(Common::swap32(hid_request.interrupt.length));
|
||||||
|
endpoint = static_cast<u8>(Common::swap32(hid_request.interrupt.endpoint));
|
||||||
|
data_address = Common::swap32(hid_request.data_addr);
|
||||||
|
}
|
||||||
|
} // namespace USB
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
50
Source/Core/Core/IOS/USB/USBV4.h
Normal file
50
Source/Core/Core/IOS/USB/USBV4.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
|
||||||
|
// Used by an early version of /dev/usb/hid.
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
struct IOCtlRequest;
|
||||||
|
|
||||||
|
namespace USB
|
||||||
|
{
|
||||||
|
enum V4Requests
|
||||||
|
{
|
||||||
|
IOCTL_USBV4_GETDEVICECHANGE = 0,
|
||||||
|
IOCTL_USBV4_SET_SUSPEND = 1,
|
||||||
|
IOCTL_USBV4_CTRLMSG = 2,
|
||||||
|
IOCTL_USBV4_INTRMSG_IN = 3,
|
||||||
|
IOCTL_USBV4_INTRMSG_OUT = 4,
|
||||||
|
IOCTL_USBV4_GET_US_STRING = 5,
|
||||||
|
IOCTL_USBV4_GETVERSION = 6,
|
||||||
|
IOCTL_USBV4_SHUTDOWN = 7,
|
||||||
|
IOCTL_USBV4_CANCELINTERRUPT = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V4CtrlMessage final : CtrlMessage
|
||||||
|
{
|
||||||
|
explicit V4CtrlMessage(const IOCtlRequest& ioctl);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V4GetUSStringMessage final : CtrlMessage
|
||||||
|
{
|
||||||
|
explicit V4GetUSStringMessage(const IOCtlRequest& ioctl);
|
||||||
|
void OnTransferComplete() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V4IntrMessage final : IntrMessage
|
||||||
|
{
|
||||||
|
explicit V4IntrMessage(const IOCtlRequest& ioctl);
|
||||||
|
};
|
||||||
|
} // namespace USB
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
55
Source/Core/Core/IOS/USB/USBV5.cpp
Normal file
55
Source/Core/Core/IOS/USB/USBV5.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/USB/USBV5.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace USB
|
||||||
|
{
|
||||||
|
V5CtrlMessage::V5CtrlMessage(const IOCtlVRequest& ioctlv)
|
||||||
|
: CtrlMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 16))
|
||||||
|
{
|
||||||
|
request_type = Memory::Read_U8(ioctlv.in_vectors[0].address + 8);
|
||||||
|
request = Memory::Read_U8(ioctlv.in_vectors[0].address + 9);
|
||||||
|
value = Memory::Read_U16(ioctlv.in_vectors[0].address + 10);
|
||||||
|
index = Memory::Read_U16(ioctlv.in_vectors[0].address + 12);
|
||||||
|
length = Memory::Read_U16(ioctlv.in_vectors[0].address + 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
V5BulkMessage::V5BulkMessage(const IOCtlVRequest& ioctlv)
|
||||||
|
: BulkMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8))
|
||||||
|
{
|
||||||
|
length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12);
|
||||||
|
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
V5IntrMessage::V5IntrMessage(const IOCtlVRequest& ioctlv)
|
||||||
|
: IntrMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8))
|
||||||
|
{
|
||||||
|
length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12);
|
||||||
|
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
V5IsoMessage::V5IsoMessage(const IOCtlVRequest& ioctlv)
|
||||||
|
: IsoMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8))
|
||||||
|
{
|
||||||
|
num_packets = Memory::Read_U8(ioctlv.in_vectors[0].address + 16);
|
||||||
|
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 17);
|
||||||
|
packet_sizes_addr = Memory::Read_U32(ioctlv.in_vectors[0].address + 12);
|
||||||
|
for (size_t i = 0; i < num_packets; ++i)
|
||||||
|
packet_sizes.push_back(Memory::Read_U16(static_cast<u32>(packet_sizes_addr + i * sizeof(u16))));
|
||||||
|
length = std::accumulate(packet_sizes.begin(), packet_sizes.end(), 0);
|
||||||
|
}
|
||||||
|
} // namespace USB
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
58
Source/Core/Core/IOS/USB/USBV5.h
Normal file
58
Source/Core/Core/IOS/USB/USBV5.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
|
||||||
|
// Used by late USB interfaces for /dev/usb/ven and /dev/usb/hid (since IOS57 which
|
||||||
|
// reorganised the USB modules in IOS).
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
struct IOCtlRequest;
|
||||||
|
|
||||||
|
namespace USB
|
||||||
|
{
|
||||||
|
enum V5Requests
|
||||||
|
{
|
||||||
|
IOCTL_USBV5_GETVERSION = 0,
|
||||||
|
IOCTL_USBV5_GETDEVICECHANGE = 1,
|
||||||
|
IOCTL_USBV5_SHUTDOWN = 2,
|
||||||
|
IOCTL_USBV5_GETDEVPARAMS = 3,
|
||||||
|
IOCTL_USBV5_ATTACHFINISH = 6,
|
||||||
|
IOCTL_USBV5_SETALTERNATE = 7,
|
||||||
|
IOCTL_USBV5_SUSPEND_RESUME = 16,
|
||||||
|
IOCTL_USBV5_CANCELENDPOINT = 17,
|
||||||
|
IOCTLV_USBV5_CTRLMSG = 18,
|
||||||
|
IOCTLV_USBV5_INTRMSG = 19,
|
||||||
|
IOCTLV_USBV5_ISOMSG = 20,
|
||||||
|
IOCTLV_USBV5_BULKMSG = 21
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V5CtrlMessage final : CtrlMessage
|
||||||
|
{
|
||||||
|
explicit V5CtrlMessage(const IOCtlVRequest& ioctlv);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V5BulkMessage final : BulkMessage
|
||||||
|
{
|
||||||
|
explicit V5BulkMessage(const IOCtlVRequest& ioctlv);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V5IntrMessage final : IntrMessage
|
||||||
|
{
|
||||||
|
explicit V5IntrMessage(const IOCtlVRequest& ioctlv);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct V5IsoMessage final : IsoMessage
|
||||||
|
{
|
||||||
|
explicit V5IsoMessage(const IOCtlVRequest& cmd_buffer);
|
||||||
|
};
|
||||||
|
} // namespace USB
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
230
Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp
Normal file
230
Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "Common/Align.h"
|
||||||
|
#include "Common/ChunkFile.h"
|
||||||
|
#include "Common/CommonFuncs.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
#include "Core/IOS/USB/USBV4.h"
|
||||||
|
#include "Core/IOS/USB/USB_HID/HIDv4.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
USB_HIDv4::USB_HIDv4(u32 device_id, const std::string& device_name)
|
||||||
|
: USBHost(device_id, device_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
USB_HIDv4::~USB_HIDv4()
|
||||||
|
{
|
||||||
|
StopThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
request.Log(GetDeviceName(), LogTypes::IOS_USB);
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTL_USBV4_GETVERSION:
|
||||||
|
return GetDefaultReply(VERSION);
|
||||||
|
case USB::IOCTL_USBV4_GETDEVICECHANGE:
|
||||||
|
return GetDeviceChange(request);
|
||||||
|
case USB::IOCTL_USBV4_SHUTDOWN:
|
||||||
|
return Shutdown(request);
|
||||||
|
case USB::IOCTL_USBV4_SET_SUSPEND:
|
||||||
|
// Not implemented in IOS
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
case USB::IOCTL_USBV4_CANCELINTERRUPT:
|
||||||
|
return CancelInterrupt(request);
|
||||||
|
case USB::IOCTL_USBV4_GET_US_STRING:
|
||||||
|
case USB::IOCTL_USBV4_CTRLMSG:
|
||||||
|
case USB::IOCTL_USBV4_INTRMSG_IN:
|
||||||
|
case USB::IOCTL_USBV4_INTRMSG_OUT:
|
||||||
|
{
|
||||||
|
if (request.buffer_in == 0 || request.buffer_in_size != 32)
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
const auto device = GetDeviceByIOSID(Memory::Read_U32(request.buffer_in + 16));
|
||||||
|
if (!device->Attach(0))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
return HandleTransfer(device, request.request,
|
||||||
|
[&, this]() { return SubmitTransfer(*device, request); });
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_HIDv4::CancelInterrupt(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
if (request.buffer_in == 0 || request.buffer_in_size != 8)
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
auto device = GetDeviceByIOSID(Memory::Read_U32(request.buffer_in));
|
||||||
|
if (!device)
|
||||||
|
return GetDefaultReply(IPC_ENOENT);
|
||||||
|
device->CancelTransfer(Memory::Read_U8(request.buffer_in + 4));
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_HIDv4::GetDeviceChange(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||||
|
if (request.buffer_out == 0 || request.buffer_out_size != 0x600)
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
m_devicechange_hook_request = std::make_unique<IOCtlRequest>(request.address);
|
||||||
|
// On the first call, the reply is sent immediately (instead of on device insertion/removal)
|
||||||
|
if (m_devicechange_first_call)
|
||||||
|
{
|
||||||
|
TriggerDeviceChangeReply();
|
||||||
|
m_devicechange_first_call = false;
|
||||||
|
}
|
||||||
|
return GetNoReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_HIDv4::Shutdown(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||||
|
if (m_devicechange_hook_request != 0)
|
||||||
|
{
|
||||||
|
Memory::Write_U32(0xffffffff, m_devicechange_hook_request->buffer_out);
|
||||||
|
EnqueueReply(*m_devicechange_hook_request, -1);
|
||||||
|
m_devicechange_hook_request.reset();
|
||||||
|
}
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 USB_HIDv4::SubmitTransfer(USB::Device& device, const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTL_USBV4_CTRLMSG:
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V4CtrlMessage>(request));
|
||||||
|
case USB::IOCTL_USBV4_GET_US_STRING:
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V4GetUSStringMessage>(request));
|
||||||
|
case USB::IOCTL_USBV4_INTRMSG_IN:
|
||||||
|
case USB::IOCTL_USBV4_INTRMSG_OUT:
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V4IntrMessage>(request));
|
||||||
|
default:
|
||||||
|
return IPC_EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_HIDv4::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
p.Do(m_devicechange_first_call);
|
||||||
|
u32 hook_address = m_devicechange_hook_request ? m_devicechange_hook_request->address : 0;
|
||||||
|
p.Do(hook_address);
|
||||||
|
if (hook_address != 0)
|
||||||
|
m_devicechange_hook_request = std::make_unique<IOCtlRequest>(hook_address);
|
||||||
|
else
|
||||||
|
m_devicechange_hook_request.reset();
|
||||||
|
|
||||||
|
p.Do(m_ios_ids);
|
||||||
|
p.Do(m_device_ids);
|
||||||
|
|
||||||
|
USBHost::DoState(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<USB::Device> USB_HIDv4::GetDeviceByIOSID(const s32 ios_id) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_id_map_mutex};
|
||||||
|
const auto iterator = m_ios_ids.find(ios_id);
|
||||||
|
if (iterator == m_ios_ids.cend())
|
||||||
|
return nullptr;
|
||||||
|
return GetDeviceById(iterator->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_HIDv4::OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> device)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> id_map_lock{m_id_map_mutex};
|
||||||
|
if (event == ChangeEvent::Inserted)
|
||||||
|
{
|
||||||
|
s32 new_id = 0;
|
||||||
|
while (m_ios_ids.find(new_id) != m_ios_ids.cend())
|
||||||
|
++new_id;
|
||||||
|
m_ios_ids[new_id] = device->GetId();
|
||||||
|
m_device_ids[device->GetId()] = new_id;
|
||||||
|
}
|
||||||
|
else if (event == ChangeEvent::Removed &&
|
||||||
|
m_device_ids.find(device->GetId()) != m_device_ids.cend())
|
||||||
|
{
|
||||||
|
m_ios_ids.erase(m_device_ids.at(device->GetId()));
|
||||||
|
m_device_ids.erase(device->GetId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||||
|
TriggerDeviceChangeReply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USB_HIDv4::ShouldAddDevice(const USB::Device& device) const
|
||||||
|
{
|
||||||
|
return device.HasClass(HID_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_HIDv4::TriggerDeviceChangeReply()
|
||||||
|
{
|
||||||
|
if (!m_devicechange_hook_request)
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||||
|
const u32 dest = m_devicechange_hook_request->buffer_out;
|
||||||
|
u32 offset = 0;
|
||||||
|
for (const auto& device : m_devices)
|
||||||
|
{
|
||||||
|
const std::vector<u8> device_section = GetDeviceEntry(*device.second.get());
|
||||||
|
if (offset + device_section.size() > m_devicechange_hook_request->buffer_out_size - 1)
|
||||||
|
{
|
||||||
|
WARN_LOG(IOS_USB, "Too many devices connected, skipping");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Memory::CopyToEmu(dest + offset, device_section.data(), device_section.size());
|
||||||
|
offset += Common::AlignUp(static_cast<u32>(device_section.size()), 4);
|
||||||
|
}
|
||||||
|
// IOS writes 0xffffffff to the buffer when there are no more devices
|
||||||
|
Memory::Write_U32(0xffffffff, dest + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
EnqueueReply(*m_devicechange_hook_request, IPC_SUCCESS, 0, CoreTiming::FromThread::ANY);
|
||||||
|
m_devicechange_hook_request.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> USB_HIDv4::GetDeviceEntry(const USB::Device& device) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> id_map_lock{m_id_map_mutex};
|
||||||
|
|
||||||
|
// The structure for a device section is as follows:
|
||||||
|
// 0-4 bytes: total size of the device data, including the size and the device ID
|
||||||
|
// 4-8 bytes: device ID
|
||||||
|
// the rest of the buffer is device descriptors data
|
||||||
|
std::vector<u8> entry(8);
|
||||||
|
const std::vector<u8> descriptors = device.GetDescriptorsUSBV4();
|
||||||
|
const u32 entry_size = Common::swap32(static_cast<u32>(entry.size() + descriptors.size()));
|
||||||
|
const u32 ios_device_id = Common::swap32(m_device_ids.at(device.GetId()));
|
||||||
|
std::memcpy(entry.data(), &entry_size, sizeof(entry_size));
|
||||||
|
std::memcpy(entry.data() + 4, &ios_device_id, sizeof(ios_device_id));
|
||||||
|
entry.insert(entry.end(), descriptors.begin(), descriptors.end());
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
62
Source/Core/Core/IOS/USB/USB_HID/HIDv4.h
Normal file
62
Source/Core/Core/IOS/USB/USB_HID/HIDv4.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/IPC.h"
|
||||||
|
#include "Core/IOS/USB/Host.h"
|
||||||
|
|
||||||
|
class PointerWrap;
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
class USB_HIDv4 final : public USBHost
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
USB_HIDv4(u32 device_id, const std::string& device_name);
|
||||||
|
~USB_HIDv4() override;
|
||||||
|
|
||||||
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
|
|
||||||
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<USB::Device> GetDeviceByIOSID(s32 ios_id) const;
|
||||||
|
|
||||||
|
IPCCommandResult CancelInterrupt(const IOCtlRequest& request);
|
||||||
|
IPCCommandResult GetDeviceChange(const IOCtlRequest& request);
|
||||||
|
IPCCommandResult Shutdown(const IOCtlRequest& request);
|
||||||
|
s32 SubmitTransfer(USB::Device& device, const IOCtlRequest& request);
|
||||||
|
|
||||||
|
void TriggerDeviceChangeReply();
|
||||||
|
std::vector<u8> GetDeviceEntry(const USB::Device& device) const;
|
||||||
|
void OnDeviceChange(ChangeEvent, std::shared_ptr<USB::Device>) override;
|
||||||
|
bool ShouldAddDevice(const USB::Device& device) const override;
|
||||||
|
|
||||||
|
static constexpr u32 VERSION = 0x40001;
|
||||||
|
static constexpr u8 HID_CLASS = 0x03;
|
||||||
|
|
||||||
|
bool m_devicechange_first_call = true;
|
||||||
|
std::mutex m_devicechange_hook_address_mutex;
|
||||||
|
std::unique_ptr<IOCtlRequest> m_devicechange_hook_request;
|
||||||
|
|
||||||
|
mutable std::mutex m_id_map_mutex;
|
||||||
|
// IOS device IDs <=> USB device IDs
|
||||||
|
std::map<s32, u64> m_ios_ids;
|
||||||
|
std::map<u64, s32> m_device_ids;
|
||||||
|
};
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
@ -1,575 +0,0 @@
|
|||||||
// Copyright 2012 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <libusb.h>
|
|
||||||
|
|
||||||
#include "Common/Align.h"
|
|
||||||
#include "Common/CommonFuncs.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Core/Core.h"
|
|
||||||
#include "Core/CoreTiming.h"
|
|
||||||
#include "Core/Debugger/Debugger_SymbolMap.h"
|
|
||||||
#include "Core/HW/Memmap.h"
|
|
||||||
#include "Core/IOS/IPC.h"
|
|
||||||
#include "Core/IOS/USB/USB_HIDv4.h"
|
|
||||||
|
|
||||||
namespace IOS
|
|
||||||
{
|
|
||||||
namespace HLE
|
|
||||||
{
|
|
||||||
namespace Device
|
|
||||||
{
|
|
||||||
constexpr int MAX_DEVICE_DEVNUM = 256;
|
|
||||||
static u64 hidDeviceAliases[MAX_DEVICE_DEVNUM];
|
|
||||||
|
|
||||||
// Regular thread
|
|
||||||
void USB_HIDv4::checkUsbUpdates(USB_HIDv4* hid)
|
|
||||||
{
|
|
||||||
timeval tv;
|
|
||||||
tv.tv_sec = 0;
|
|
||||||
tv.tv_usec = 500;
|
|
||||||
while (hid->usb_thread_running)
|
|
||||||
{
|
|
||||||
static u16 timeToFill = 0;
|
|
||||||
if (timeToFill == 0)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(hid->m_device_list_reply_mutex);
|
|
||||||
if (hid->deviceCommandAddress != 0)
|
|
||||||
{
|
|
||||||
IOCtlRequest request{hid->deviceCommandAddress};
|
|
||||||
hid->FillOutDevices(request);
|
|
||||||
EnqueueReply(request, IPC_SUCCESS, 0, CoreTiming::FromThread::NON_CPU);
|
|
||||||
hid->deviceCommandAddress = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timeToFill += 8;
|
|
||||||
libusb_handle_events_timeout(nullptr, &tv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void USB_HIDv4::handleUsbUpdates(struct libusb_transfer* transfer)
|
|
||||||
{
|
|
||||||
s32 ret = IPC_EINVAL;
|
|
||||||
u32 replyAddress = (u32)(size_t)transfer->user_data;
|
|
||||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
|
|
||||||
{
|
|
||||||
ret = transfer->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
IOCtlRequest request{replyAddress};
|
|
||||||
EnqueueReply(request, ret, 0, CoreTiming::FromThread::NON_CPU);
|
|
||||||
}
|
|
||||||
|
|
||||||
USB_HIDv4::USB_HIDv4(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
|
|
||||||
{
|
|
||||||
deviceCommandAddress = 0;
|
|
||||||
memset(hidDeviceAliases, 0, sizeof(hidDeviceAliases));
|
|
||||||
int ret = libusb_init(nullptr);
|
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_HID, "libusb_init failed with error: %d", ret);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usb_thread_running = true;
|
|
||||||
usb_thread = std::thread(checkUsbUpdates, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
USB_HIDv4::~USB_HIDv4()
|
|
||||||
{
|
|
||||||
bool deinit_libusb = false;
|
|
||||||
if (usb_thread_running)
|
|
||||||
{
|
|
||||||
usb_thread_running = false;
|
|
||||||
usb_thread.join();
|
|
||||||
deinit_libusb = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& device : m_open_devices)
|
|
||||||
{
|
|
||||||
libusb_close(device.second);
|
|
||||||
}
|
|
||||||
m_open_devices.clear();
|
|
||||||
|
|
||||||
if (deinit_libusb)
|
|
||||||
libusb_exit(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request)
|
|
||||||
{
|
|
||||||
if (Core::g_want_determinism)
|
|
||||||
{
|
|
||||||
return GetDefaultReply(IPC_EACCES);
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 return_value = IPC_SUCCESS;
|
|
||||||
switch (request.request)
|
|
||||||
{
|
|
||||||
case IOCTL_HID_GET_ATTACHED:
|
|
||||||
{
|
|
||||||
deviceCommandAddress = request.address;
|
|
||||||
return GetNoReply();
|
|
||||||
}
|
|
||||||
case IOCTL_HID_OPEN:
|
|
||||||
{
|
|
||||||
// hid version, apparently
|
|
||||||
return_value = 0x40001;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IOCTL_HID_SET_SUSPEND:
|
|
||||||
{
|
|
||||||
// not actually implemented in IOS
|
|
||||||
return_value = IPC_SUCCESS;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IOCTL_HID_CANCEL_INTERRUPT:
|
|
||||||
{
|
|
||||||
return_value = IPC_SUCCESS;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IOCTL_HID_CONTROL:
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
ERROR CODES:
|
|
||||||
-4 Can't find device specified
|
|
||||||
*/
|
|
||||||
|
|
||||||
u32 dev_num = Memory::Read_U32(request.buffer_in + 0x10);
|
|
||||||
u8 bmRequestType = Memory::Read_U8(request.buffer_in + 0x14);
|
|
||||||
u8 bRequest = Memory::Read_U8(request.buffer_in + 0x15);
|
|
||||||
u16 wValue = Memory::Read_U16(request.buffer_in + 0x16);
|
|
||||||
u16 wIndex = Memory::Read_U16(request.buffer_in + 0x18);
|
|
||||||
u16 wLength = Memory::Read_U16(request.buffer_in + 0x1A);
|
|
||||||
u32 data = Memory::Read_U32(request.buffer_in + 0x1C);
|
|
||||||
|
|
||||||
return_value = IPC_EINVAL;
|
|
||||||
|
|
||||||
libusb_device_handle* dev_handle = GetDeviceByDevNum(dev_num);
|
|
||||||
|
|
||||||
if (dev_handle == nullptr)
|
|
||||||
{
|
|
||||||
INFO_LOG(IOS_HID, "Could not find handle: %X", dev_num);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
struct libusb_transfer* transfer = libusb_alloc_transfer(0);
|
|
||||||
transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
|
|
||||||
|
|
||||||
u8* buffer = (u8*)malloc(wLength + LIBUSB_CONTROL_SETUP_SIZE);
|
|
||||||
libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, wLength);
|
|
||||||
Memory::CopyFromEmu(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength);
|
|
||||||
libusb_fill_control_transfer(transfer, dev_handle, buffer, handleUsbUpdates,
|
|
||||||
(void*)(size_t)request.address, /* no timeout */ 0);
|
|
||||||
libusb_submit_transfer(transfer);
|
|
||||||
|
|
||||||
// DEBUG_LOG(IOS_HID, "HID::IOCtl(Control)(%02X, %02X) (BufferIn: (%08x, %i),
|
|
||||||
// request.buffer_out:
|
|
||||||
// (%08x, %i)",
|
|
||||||
// bmRequestType, bRequest, BufferIn, request.buffer_in_size, request.buffer_out,
|
|
||||||
// request.buffer_out_size);
|
|
||||||
|
|
||||||
// It's the async way!
|
|
||||||
return GetNoReply();
|
|
||||||
}
|
|
||||||
case IOCTL_HID_INTERRUPT_OUT:
|
|
||||||
case IOCTL_HID_INTERRUPT_IN:
|
|
||||||
{
|
|
||||||
u32 dev_num = Memory::Read_U32(request.buffer_in + 0x10);
|
|
||||||
u32 endpoint = Memory::Read_U32(request.buffer_in + 0x14);
|
|
||||||
u32 length = Memory::Read_U32(request.buffer_in + 0x18);
|
|
||||||
|
|
||||||
u32 data = Memory::Read_U32(request.buffer_in + 0x1C);
|
|
||||||
|
|
||||||
return_value = IPC_EINVAL;
|
|
||||||
|
|
||||||
libusb_device_handle* dev_handle = GetDeviceByDevNum(dev_num);
|
|
||||||
|
|
||||||
if (dev_handle == nullptr)
|
|
||||||
{
|
|
||||||
INFO_LOG(IOS_HID, "Could not find handle: %X", dev_num);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct libusb_transfer* transfer = libusb_alloc_transfer(0);
|
|
||||||
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
|
||||||
libusb_fill_interrupt_transfer(transfer, dev_handle, endpoint, Memory::GetPointer(data), length,
|
|
||||||
handleUsbUpdates, (void*)(size_t)request.address, 0);
|
|
||||||
libusb_submit_transfer(transfer);
|
|
||||||
|
|
||||||
// It's the async way!
|
|
||||||
return GetNoReply();
|
|
||||||
}
|
|
||||||
case IOCTL_HID_SHUTDOWN:
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(m_device_list_reply_mutex);
|
|
||||||
if (deviceCommandAddress != 0)
|
|
||||||
{
|
|
||||||
IOCtlRequest pending_request{deviceCommandAddress};
|
|
||||||
Memory::Write_U32(0xFFFFFFFF, pending_request.buffer_out);
|
|
||||||
EnqueueReply(pending_request, -1);
|
|
||||||
deviceCommandAddress = 0;
|
|
||||||
}
|
|
||||||
INFO_LOG(IOS_HID, "HID::IOCtl(Shutdown) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
|
|
||||||
request.buffer_in, request.buffer_in_size, request.buffer_out,
|
|
||||||
request.buffer_out_size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
request.Log(GetDeviceName(), LogTypes::IOS_HID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetDefaultReply(return_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool USB_HIDv4::ClaimDevice(libusb_device_handle* dev)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
if ((ret = libusb_kernel_driver_active(dev, 0)) == 1)
|
|
||||||
{
|
|
||||||
if ((ret = libusb_detach_kernel_driver(dev, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_HID, "libusb_detach_kernel_driver failed with error: %d", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_HID, "libusb_kernel_driver_active error ret = %d", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ret = libusb_claim_interface(dev, 0)))
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_HID, "libusb_claim_interface failed with error: %d", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPCCommandResult USB_HIDv4::IOCtlV(const IOCtlVRequest& request)
|
|
||||||
{
|
|
||||||
Dolphin_Debugger::PrintCallstack(LogTypes::IOS_HID, LogTypes::LWARNING);
|
|
||||||
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_HID);
|
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void USB_HIDv4::ConvertDeviceToWii(USB::DeviceDescriptor* dest, const libusb_device_descriptor* src)
|
|
||||||
{
|
|
||||||
dest->bLength = src->bLength;
|
|
||||||
dest->bDescriptorType = src->bDescriptorType;
|
|
||||||
dest->bcdUSB = Common::swap16(src->bcdUSB);
|
|
||||||
dest->bDeviceClass = src->bDeviceClass;
|
|
||||||
dest->bDeviceSubClass = src->bDeviceSubClass;
|
|
||||||
dest->bDeviceProtocol = src->bDeviceProtocol;
|
|
||||||
dest->bMaxPacketSize0 = src->bMaxPacketSize0;
|
|
||||||
dest->idVendor = Common::swap16(src->idVendor);
|
|
||||||
dest->idProduct = Common::swap16(src->idProduct);
|
|
||||||
dest->bcdDevice = Common::swap16(src->bcdDevice);
|
|
||||||
dest->iManufacturer = src->iManufacturer;
|
|
||||||
dest->iProduct = src->iProduct;
|
|
||||||
dest->iSerialNumber = src->iSerialNumber;
|
|
||||||
dest->bNumConfigurations = src->bNumConfigurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
void USB_HIDv4::ConvertConfigToWii(USB::ConfigDescriptor* dest, const libusb_config_descriptor* src)
|
|
||||||
{
|
|
||||||
memcpy(dest, src, sizeof(USB::ConfigDescriptor));
|
|
||||||
dest->wTotalLength = Common::swap16(dest->wTotalLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
void USB_HIDv4::ConvertInterfaceToWii(USB::InterfaceDescriptor* dest,
|
|
||||||
const libusb_interface_descriptor* src)
|
|
||||||
{
|
|
||||||
memcpy(dest, src, sizeof(USB::InterfaceDescriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
void USB_HIDv4::ConvertEndpointToWii(USB::EndpointDescriptor* dest,
|
|
||||||
const libusb_endpoint_descriptor* src)
|
|
||||||
{
|
|
||||||
memcpy(dest, src, sizeof(USB::EndpointDescriptor));
|
|
||||||
dest->wMaxPacketSize = Common::swap16(dest->wMaxPacketSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void USB_HIDv4::FillOutDevices(const IOCtlRequest& request)
|
|
||||||
{
|
|
||||||
static u16 check = 1;
|
|
||||||
int OffsetBuffer = request.buffer_out;
|
|
||||||
int OffsetStart = 0;
|
|
||||||
// int OffsetDevice = 0;
|
|
||||||
int d, c, ic, i, e; /* config, interface container, interface, endpoint */
|
|
||||||
|
|
||||||
libusb_device** list;
|
|
||||||
// libusb_device *found = nullptr;
|
|
||||||
ssize_t cnt = libusb_get_device_list(nullptr, &list);
|
|
||||||
INFO_LOG(IOS_HID, "Found %ld viable USB devices.", cnt);
|
|
||||||
for (d = 0; d < cnt; d++)
|
|
||||||
{
|
|
||||||
libusb_device* device = list[d];
|
|
||||||
struct libusb_device_descriptor desc;
|
|
||||||
int dRet = libusb_get_device_descriptor(device, &desc);
|
|
||||||
if (dRet)
|
|
||||||
{
|
|
||||||
// could not aquire the descriptor, no point in trying to use it.
|
|
||||||
WARN_LOG(IOS_HID, "libusb_get_device_descriptor failed with error: %d", dRet);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
OffsetStart = OffsetBuffer;
|
|
||||||
OffsetBuffer += 4; // skip length for now, fill at end
|
|
||||||
|
|
||||||
OffsetBuffer += 4; // skip devNum for now
|
|
||||||
|
|
||||||
USB::DeviceDescriptor wii_device;
|
|
||||||
ConvertDeviceToWii(&wii_device, &desc);
|
|
||||||
Memory::CopyToEmu(OffsetBuffer, &wii_device, wii_device.bLength);
|
|
||||||
OffsetBuffer += Common::AlignUp(wii_device.bLength, 4);
|
|
||||||
bool deviceValid = true;
|
|
||||||
bool isHID = false;
|
|
||||||
|
|
||||||
for (c = 0; deviceValid && c < desc.bNumConfigurations; c++)
|
|
||||||
{
|
|
||||||
struct libusb_config_descriptor* config = nullptr;
|
|
||||||
int cRet = libusb_get_config_descriptor(device, c, &config);
|
|
||||||
// do not try to use usb devices with more than one interface, games can crash
|
|
||||||
if (cRet == 0 && config->bNumInterfaces <= MAX_HID_INTERFACES)
|
|
||||||
{
|
|
||||||
USB::ConfigDescriptor wii_config;
|
|
||||||
ConvertConfigToWii(&wii_config, config);
|
|
||||||
Memory::CopyToEmu(OffsetBuffer, &wii_config, wii_config.bLength);
|
|
||||||
OffsetBuffer += Common::AlignUp(wii_config.bLength, 4);
|
|
||||||
|
|
||||||
for (ic = 0; ic < config->bNumInterfaces; ic++)
|
|
||||||
{
|
|
||||||
const struct libusb_interface* interfaceContainer = &config->interface[ic];
|
|
||||||
|
|
||||||
for (i = 0; i < interfaceContainer->num_altsetting; i++)
|
|
||||||
{
|
|
||||||
const struct libusb_interface_descriptor* interface =
|
|
||||||
&interfaceContainer->altsetting[i];
|
|
||||||
|
|
||||||
if (interface->bInterfaceClass == LIBUSB_CLASS_HID)
|
|
||||||
isHID = true;
|
|
||||||
|
|
||||||
USB::InterfaceDescriptor wii_interface;
|
|
||||||
ConvertInterfaceToWii(&wii_interface, interface);
|
|
||||||
Memory::CopyToEmu(OffsetBuffer, &wii_interface, wii_interface.bLength);
|
|
||||||
OffsetBuffer += Common::AlignUp(wii_interface.bLength, 4);
|
|
||||||
|
|
||||||
for (e = 0; e < interface->bNumEndpoints; e++)
|
|
||||||
{
|
|
||||||
const struct libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
|
|
||||||
|
|
||||||
USB::EndpointDescriptor wii_endpoint;
|
|
||||||
ConvertEndpointToWii(&wii_endpoint, endpoint);
|
|
||||||
Memory::CopyToEmu(OffsetBuffer, &wii_endpoint, wii_endpoint.bLength);
|
|
||||||
OffsetBuffer += Common::AlignUp(wii_endpoint.bLength, 4);
|
|
||||||
|
|
||||||
} // endpoints
|
|
||||||
} // interfaces
|
|
||||||
} // interface containters
|
|
||||||
libusb_free_config_descriptor(config);
|
|
||||||
config = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (cRet)
|
|
||||||
WARN_LOG(IOS_HID, "libusb_get_config_descriptor failed with: %d", cRet);
|
|
||||||
deviceValid = false;
|
|
||||||
OffsetBuffer = OffsetStart;
|
|
||||||
}
|
|
||||||
} // configs
|
|
||||||
|
|
||||||
if (!isHID)
|
|
||||||
{
|
|
||||||
deviceValid = false;
|
|
||||||
OffsetBuffer = OffsetStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deviceValid)
|
|
||||||
{
|
|
||||||
Memory::Write_U32(OffsetBuffer - OffsetStart, OffsetStart); // fill in length
|
|
||||||
|
|
||||||
int devNum = GetAvailableDevNum(desc.idVendor, desc.idProduct, libusb_get_bus_number(device),
|
|
||||||
libusb_get_device_address(device), check);
|
|
||||||
if (devNum < 0)
|
|
||||||
{
|
|
||||||
// too many devices to handle.
|
|
||||||
ERROR_LOG(IOS_HID, "Exhausted device list, there are way too many usb devices plugged in.");
|
|
||||||
OffsetBuffer = OffsetStart;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO_LOG(IOS_HID, "Found device with Vendor: %X Product: %X Devnum: %d", desc.idVendor,
|
|
||||||
desc.idProduct, devNum);
|
|
||||||
|
|
||||||
Memory::Write_U32(devNum, OffsetStart + 4); // write device num
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find devices that no longer exists and free them
|
|
||||||
for (i = 0; i < MAX_DEVICE_DEVNUM; i++)
|
|
||||||
{
|
|
||||||
u16 check_cur = (u16)(hidDeviceAliases[i] >> 48);
|
|
||||||
if (hidDeviceAliases[i] != 0 && check_cur != check)
|
|
||||||
{
|
|
||||||
INFO_LOG(IOS_HID, "Removing: device %d %hX %hX", i, check, check_cur);
|
|
||||||
std::lock_guard<std::mutex> lk(m_open_devices_mutex);
|
|
||||||
if (m_open_devices.find(i) != m_open_devices.end())
|
|
||||||
{
|
|
||||||
libusb_device_handle* handle = m_open_devices[i];
|
|
||||||
libusb_close(handle);
|
|
||||||
m_open_devices.erase(i);
|
|
||||||
}
|
|
||||||
hidDeviceAliases[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check++;
|
|
||||||
|
|
||||||
libusb_free_device_list(list, 1);
|
|
||||||
|
|
||||||
Memory::Write_U32(0xFFFFFFFF, OffsetBuffer); // no more devices
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_device_handle* USB_HIDv4::GetDeviceByDevNum(u32 devNum)
|
|
||||||
{
|
|
||||||
libusb_device** list;
|
|
||||||
libusb_device_handle* handle = nullptr;
|
|
||||||
ssize_t cnt;
|
|
||||||
|
|
||||||
if (devNum >= MAX_DEVICE_DEVNUM)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lk(m_open_devices_mutex);
|
|
||||||
|
|
||||||
if (m_open_devices.find(devNum) != m_open_devices.end())
|
|
||||||
{
|
|
||||||
handle = m_open_devices[devNum];
|
|
||||||
if (libusb_kernel_driver_active(handle, 0) != LIBUSB_ERROR_NO_DEVICE)
|
|
||||||
{
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
libusb_close(handle);
|
|
||||||
m_open_devices.erase(devNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt = libusb_get_device_list(nullptr, &list);
|
|
||||||
|
|
||||||
if (cnt < 0)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
static bool has_warned_about_drivers = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (ssize_t i = 0; i < cnt; i++)
|
|
||||||
{
|
|
||||||
libusb_device* device = list[i];
|
|
||||||
struct libusb_device_descriptor desc;
|
|
||||||
int dRet = libusb_get_device_descriptor(device, &desc);
|
|
||||||
u8 bus = libusb_get_bus_number(device);
|
|
||||||
u8 port = libusb_get_device_address(device);
|
|
||||||
u64 unique_id =
|
|
||||||
((u64)desc.idVendor << 32) | ((u64)desc.idProduct << 16) | ((u64)bus << 8) | (u64)port;
|
|
||||||
if ((hidDeviceAliases[devNum] & HID_ID_MASK) == unique_id)
|
|
||||||
{
|
|
||||||
int ret = libusb_open(device, &handle);
|
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
if (ret == LIBUSB_ERROR_ACCESS)
|
|
||||||
{
|
|
||||||
if (dRet)
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_HID, "Dolphin does not have access to this device: Bus %03d Device "
|
|
||||||
"%03d: ID ????:???? (couldn't get id).",
|
|
||||||
bus, port);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ERROR_LOG(
|
|
||||||
IOS_HID,
|
|
||||||
"Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.",
|
|
||||||
bus, port, desc.idVendor, desc.idProduct);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
|
||||||
else if (ret == LIBUSB_ERROR_NOT_SUPPORTED)
|
|
||||||
{
|
|
||||||
if (!has_warned_about_drivers)
|
|
||||||
{
|
|
||||||
// Max of one warning.
|
|
||||||
has_warned_about_drivers = true;
|
|
||||||
WARN_LOG(IOS_HID, "Please install the libusb drivers for the device %04X:%04X",
|
|
||||||
desc.idVendor, desc.idProduct);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_HID, "libusb_open failed to open device with error = %d", ret);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ClaimDevice(handle))
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_HID, "Could not claim the device for handle: %X", devNum);
|
|
||||||
libusb_close(handle);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_open_devices[devNum] = handle;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
handle = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_free_device_list(list, 1);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
int USB_HIDv4::GetAvailableDevNum(u16 idVendor, u16 idProduct, u8 bus, u8 port, u16 check)
|
|
||||||
{
|
|
||||||
int pos = -1;
|
|
||||||
u64 unique_id = ((u64)idVendor << 32) | ((u64)idProduct << 16) | ((u64)bus << 8) | (u64)port;
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_DEVICE_DEVNUM; i++)
|
|
||||||
{
|
|
||||||
u64 id = hidDeviceAliases[i] & HID_ID_MASK;
|
|
||||||
if (id == 0 && pos == -1)
|
|
||||||
{
|
|
||||||
pos = i;
|
|
||||||
}
|
|
||||||
else if (id == unique_id)
|
|
||||||
{
|
|
||||||
hidDeviceAliases[i] = id | ((u64)check << 48);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos != -1)
|
|
||||||
{
|
|
||||||
hidDeviceAliases[pos] = unique_id | ((u64)check << 48);
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} // namespace Device
|
|
||||||
} // namespace HLE
|
|
||||||
} // namespace IOS
|
|
@ -1,89 +0,0 @@
|
|||||||
// Copyright 2012 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Core/IOS/Device.h"
|
|
||||||
#include "Core/IOS/IPC.h"
|
|
||||||
#include "Core/IOS/USB/Common.h"
|
|
||||||
|
|
||||||
// Forward declare things which we need from libusb header.
|
|
||||||
// This prevents users of this file from indirectly pulling in libusb.
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#define LIBUSB_CALL WINAPI
|
|
||||||
#else
|
|
||||||
#define LIBUSB_CALL
|
|
||||||
#endif
|
|
||||||
struct libusb_config_descriptor;
|
|
||||||
struct libusb_device_descriptor;
|
|
||||||
struct libusb_device_handle;
|
|
||||||
struct libusb_endpoint_descriptor;
|
|
||||||
struct libusb_interface_descriptor;
|
|
||||||
struct libusb_transfer;
|
|
||||||
|
|
||||||
namespace IOS
|
|
||||||
{
|
|
||||||
namespace HLE
|
|
||||||
{
|
|
||||||
#define HID_ID_MASK 0x0000FFFFFFFFFFFF
|
|
||||||
#define MAX_HID_INTERFACES 1
|
|
||||||
|
|
||||||
namespace Device
|
|
||||||
{
|
|
||||||
class USB_HIDv4 : public Device
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
USB_HIDv4(u32 _DeviceID, const std::string& _rDeviceName);
|
|
||||||
|
|
||||||
virtual ~USB_HIDv4();
|
|
||||||
|
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
IOCTL_HID_GET_ATTACHED = 0x00,
|
|
||||||
IOCTL_HID_SET_SUSPEND = 0x01,
|
|
||||||
IOCTL_HID_CONTROL = 0x02,
|
|
||||||
IOCTL_HID_INTERRUPT_IN = 0x03,
|
|
||||||
IOCTL_HID_INTERRUPT_OUT = 0x04,
|
|
||||||
IOCTL_HID_GET_US_STRING = 0x05,
|
|
||||||
IOCTL_HID_OPEN = 0x06,
|
|
||||||
IOCTL_HID_SHUTDOWN = 0x07,
|
|
||||||
IOCTL_HID_CANCEL_INTERRUPT = 0x08,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 deviceCommandAddress;
|
|
||||||
void FillOutDevices(const IOCtlRequest& request);
|
|
||||||
int GetAvailableDevNum(u16 idVendor, u16 idProduct, u8 bus, u8 port, u16 check);
|
|
||||||
bool ClaimDevice(libusb_device_handle* dev);
|
|
||||||
|
|
||||||
void ConvertDeviceToWii(USB::DeviceDescriptor* dest, const libusb_device_descriptor* src);
|
|
||||||
void ConvertConfigToWii(USB::ConfigDescriptor* dest, const libusb_config_descriptor* src);
|
|
||||||
void ConvertInterfaceToWii(USB::InterfaceDescriptor* dest,
|
|
||||||
const libusb_interface_descriptor* src);
|
|
||||||
void ConvertEndpointToWii(USB::EndpointDescriptor* dest, const libusb_endpoint_descriptor* src);
|
|
||||||
|
|
||||||
static void checkUsbUpdates(USB_HIDv4* hid);
|
|
||||||
static void LIBUSB_CALL handleUsbUpdates(libusb_transfer* transfer);
|
|
||||||
|
|
||||||
libusb_device_handle* GetDeviceByDevNum(u32 devNum);
|
|
||||||
std::map<u32, libusb_device_handle*> m_open_devices;
|
|
||||||
std::mutex m_open_devices_mutex;
|
|
||||||
std::mutex m_device_list_reply_mutex;
|
|
||||||
|
|
||||||
std::thread usb_thread;
|
|
||||||
bool usb_thread_running;
|
|
||||||
};
|
|
||||||
} // namespace Device
|
|
||||||
} // namespace HLE
|
|
||||||
} // namespace IOS
|
|
@ -1,83 +0,0 @@
|
|||||||
// Copyright 2016 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/IOS/USB/USB_VEN.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Core/HW/Memmap.h"
|
|
||||||
|
|
||||||
namespace IOS
|
|
||||||
{
|
|
||||||
namespace HLE
|
|
||||||
{
|
|
||||||
namespace Device
|
|
||||||
{
|
|
||||||
USB_VEN::USB_VEN(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request)
|
|
||||||
{
|
|
||||||
request.Dump(GetDeviceName());
|
|
||||||
return GetNoReply();
|
|
||||||
}
|
|
||||||
|
|
||||||
IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request)
|
|
||||||
{
|
|
||||||
request.Log(GetDeviceName(), LogTypes::OSHLE);
|
|
||||||
|
|
||||||
IPCCommandResult reply = GetDefaultReply(IPC_SUCCESS);
|
|
||||||
switch (request.request)
|
|
||||||
{
|
|
||||||
case USBV5_IOCTL_GETVERSION:
|
|
||||||
Memory::Write_U32(0x50001, request.buffer_out);
|
|
||||||
reply = GetDefaultReply(IPC_SUCCESS);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USBV5_IOCTL_GETDEVICECHANGE:
|
|
||||||
{
|
|
||||||
// sent on change
|
|
||||||
static bool firstcall = true;
|
|
||||||
if (firstcall)
|
|
||||||
{
|
|
||||||
reply = GetDefaultReply(IPC_SUCCESS);
|
|
||||||
firstcall = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// num devices
|
|
||||||
reply = GetDefaultReply(0);
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USBV5_IOCTL_ATTACHFINISH:
|
|
||||||
reply = GetDefaultReply(IPC_SUCCESS);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USBV5_IOCTL_SUSPEND_RESUME:
|
|
||||||
DEBUG_LOG(OSHLE, "Device: %i Resumed: %i", Memory::Read_U32(request.buffer_in),
|
|
||||||
Memory::Read_U32(request.buffer_in + 4));
|
|
||||||
reply = GetDefaultReply(IPC_SUCCESS);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USBV5_IOCTL_GETDEVPARAMS:
|
|
||||||
{
|
|
||||||
s32 device = Memory::Read_U32(request.buffer_in);
|
|
||||||
u32 unk = Memory::Read_U32(request.buffer_in + 4);
|
|
||||||
|
|
||||||
DEBUG_LOG(OSHLE, "USBV5_IOCTL_GETDEVPARAMS device: %i unk: %i", device, unk);
|
|
||||||
|
|
||||||
Memory::Write_U32(0, request.buffer_out);
|
|
||||||
|
|
||||||
reply = GetDefaultReply(IPC_SUCCESS);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
request.Log(GetDeviceName(), LogTypes::OSHLE, LogTypes::LDEBUG);
|
|
||||||
}
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
} // namespace Device
|
|
||||||
} // namespace HLE
|
|
||||||
} // namespace IOS
|
|
@ -1,46 +0,0 @@
|
|||||||
// Copyright 2016 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Core/IOS/Device.h"
|
|
||||||
#include "Core/IOS/IPC.h"
|
|
||||||
|
|
||||||
namespace IOS
|
|
||||||
{
|
|
||||||
namespace HLE
|
|
||||||
{
|
|
||||||
namespace Device
|
|
||||||
{
|
|
||||||
class USB_VEN final : public Device
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
USB_VEN(u32 device_id, const std::string& device_name);
|
|
||||||
|
|
||||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum USBIOCtl
|
|
||||||
{
|
|
||||||
USBV5_IOCTL_GETVERSION = 0,
|
|
||||||
USBV5_IOCTL_GETDEVICECHANGE = 1,
|
|
||||||
USBV5_IOCTL_SHUTDOWN = 2,
|
|
||||||
USBV5_IOCTL_GETDEVPARAMS = 3,
|
|
||||||
USBV5_IOCTL_ATTACHFINISH = 6,
|
|
||||||
USBV5_IOCTL_SETALTERNATE = 7,
|
|
||||||
USBV5_IOCTL_SUSPEND_RESUME = 16,
|
|
||||||
USBV5_IOCTL_CANCELENDPOINT = 17,
|
|
||||||
USBV5_IOCTL_CTRLMSG = 18,
|
|
||||||
USBV5_IOCTL_INTRMSG = 19,
|
|
||||||
USBV5_IOCTL_ISOMSG = 20,
|
|
||||||
USBV5_IOCTL_BULKMSG = 21
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} // namespace Device
|
|
||||||
} // namespace HLE
|
|
||||||
} // namespace IOS
|
|
333
Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp
Normal file
333
Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "Common/ChunkFile.h"
|
||||||
|
#include "Common/CommonFuncs.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/USB/Common.h"
|
||||||
|
#include "Core/IOS/USB/USBV5.h"
|
||||||
|
#include "Core/IOS/USB/USB_VEN/VEN.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
USB_VEN::USB_VEN(u32 device_id, const std::string& device_name) : USBHost(device_id, device_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
USB_VEN::~USB_VEN()
|
||||||
|
{
|
||||||
|
StopThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode USB_VEN::Open(const OpenRequest& request)
|
||||||
|
{
|
||||||
|
const u32 ios_major_version = GetVersion();
|
||||||
|
if (ios_major_version != 57 && ios_major_version != 58 && ios_major_version != 59)
|
||||||
|
return IPC_ENOENT;
|
||||||
|
return USBHost::Open(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
request.Log(GetDeviceName(), LogTypes::IOS_USB);
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTL_USBV5_GETVERSION:
|
||||||
|
Memory::Write_U32(VERSION, request.buffer_out);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
case USB::IOCTL_USBV5_GETDEVICECHANGE:
|
||||||
|
return GetDeviceChange(request);
|
||||||
|
case USB::IOCTL_USBV5_SHUTDOWN:
|
||||||
|
return Shutdown(request);
|
||||||
|
case USB::IOCTL_USBV5_GETDEVPARAMS:
|
||||||
|
return HandleDeviceIOCtl(request, &USB_VEN::GetDeviceInfo);
|
||||||
|
case USB::IOCTL_USBV5_ATTACHFINISH:
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
case USB::IOCTL_USBV5_SETALTERNATE:
|
||||||
|
return HandleDeviceIOCtl(request, &USB_VEN::SetAlternateSetting);
|
||||||
|
case USB::IOCTL_USBV5_SUSPEND_RESUME:
|
||||||
|
return HandleDeviceIOCtl(request, &USB_VEN::SuspendResume);
|
||||||
|
case USB::IOCTL_USBV5_CANCELENDPOINT:
|
||||||
|
return HandleDeviceIOCtl(request, &USB_VEN::CancelEndpoint);
|
||||||
|
default:
|
||||||
|
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request)
|
||||||
|
{
|
||||||
|
static const std::map<u32, u32> s_num_vectors = {
|
||||||
|
{USB::IOCTLV_USBV5_CTRLMSG, 2},
|
||||||
|
{USB::IOCTLV_USBV5_INTRMSG, 2},
|
||||||
|
{USB::IOCTLV_USBV5_BULKMSG, 2},
|
||||||
|
{USB::IOCTLV_USBV5_ISOMSG, 4},
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (request.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTLV_USBV5_CTRLMSG:
|
||||||
|
case USB::IOCTLV_USBV5_INTRMSG:
|
||||||
|
case USB::IOCTLV_USBV5_BULKMSG:
|
||||||
|
case USB::IOCTLV_USBV5_ISOMSG:
|
||||||
|
{
|
||||||
|
if (request.in_vectors.size() + request.io_vectors.size() != s_num_vectors.at(request.request))
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
const s32 device_id = Memory::Read_U32(request.in_vectors[0].address);
|
||||||
|
auto device = GetDeviceByIOSID(device_id);
|
||||||
|
if (!device || !device->Attach(GetInterfaceNumber(device_id)))
|
||||||
|
return GetDefaultReply(IPC_ENOENT);
|
||||||
|
return HandleTransfer(device, request.request,
|
||||||
|
[&, this]() { return SubmitTransfer(*device, request); });
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_VEN::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
p.Do(m_devicechange_first_call);
|
||||||
|
u32 hook_address = m_devicechange_hook_request ? m_devicechange_hook_request->address : 0;
|
||||||
|
p.Do(hook_address);
|
||||||
|
if (hook_address != 0)
|
||||||
|
m_devicechange_hook_request = std::make_unique<IOCtlRequest>(hook_address);
|
||||||
|
else
|
||||||
|
m_devicechange_hook_request.reset();
|
||||||
|
|
||||||
|
p.Do(m_device_number);
|
||||||
|
p.Do(m_ios_ids);
|
||||||
|
p.Do(m_device_ids);
|
||||||
|
USBHost::DoState(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<USB::Device> USB_VEN::GetDeviceByIOSID(const s32 ios_id) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_id_map_mutex};
|
||||||
|
const auto iter = m_ios_ids.find(ios_id);
|
||||||
|
if (iter == m_ios_ids.cend())
|
||||||
|
return nullptr;
|
||||||
|
return GetDeviceById(iter->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 USB_VEN::GetInterfaceNumber(const s32 ios_id) const
|
||||||
|
{
|
||||||
|
const s32 id = Common::swap32(ios_id);
|
||||||
|
DeviceID device_id;
|
||||||
|
std::memcpy(&device_id, &id, sizeof(id));
|
||||||
|
return device_id.interface_plus_1e - 0x1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::CancelEndpoint(USB::Device& device, const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
const u8 endpoint = static_cast<u8>(Memory::Read_U32(request.buffer_in + 2 * sizeof(s32)));
|
||||||
|
device.CancelTransfer(endpoint);
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::GetDeviceChange(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||||
|
m_devicechange_hook_request = std::make_unique<IOCtlRequest>(request.address);
|
||||||
|
// On the first call, the reply is sent immediately (instead of on device insertion/removal)
|
||||||
|
if (m_devicechange_first_call)
|
||||||
|
{
|
||||||
|
TriggerDeviceChangeReply();
|
||||||
|
m_devicechange_first_call = false;
|
||||||
|
}
|
||||||
|
return GetNoReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::GetDeviceInfo(USB::Device& device, const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
const s32 device_id = Memory::Read_U32(request.buffer_in);
|
||||||
|
if (request.buffer_out == 0 || request.buffer_out_size != 0xc0)
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
const u8 alt_setting = Memory::Read_U8(request.buffer_in + 8);
|
||||||
|
auto descriptors = device.GetDescriptorsUSBV5(GetInterfaceNumber(device_id), alt_setting);
|
||||||
|
if (descriptors.empty())
|
||||||
|
return GetDefaultReply(IPC_ENOENT);
|
||||||
|
|
||||||
|
descriptors.resize(request.buffer_out_size - 20);
|
||||||
|
if (descriptors.size() > request.buffer_out_size - 20)
|
||||||
|
WARN_LOG(IOS_USB, "Buffer is too large. Only the first 172 bytes will be copied.");
|
||||||
|
|
||||||
|
Memory::Memset(request.buffer_out, 0, request.buffer_out_size);
|
||||||
|
Memory::Write_U32(device_id, request.buffer_out);
|
||||||
|
Memory::Write_U32(1, request.buffer_out + 4);
|
||||||
|
Memory::CopyToEmu(request.buffer_out + 20, descriptors.data(), descriptors.size());
|
||||||
|
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::SetAlternateSetting(USB::Device& device, const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
const s32 device_id = Memory::Read_U32(request.buffer_in);
|
||||||
|
if (!device.Attach(GetInterfaceNumber(device_id)))
|
||||||
|
return GetDefaultReply(-1);
|
||||||
|
|
||||||
|
const u8 alt_setting = Memory::Read_U8(request.buffer_in + 2 * sizeof(s32));
|
||||||
|
|
||||||
|
const bool success = device.SetAltSetting(alt_setting) == 0;
|
||||||
|
return GetDefaultReply(success ? IPC_SUCCESS : IPC_EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::Shutdown(const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
if (request.buffer_in != 0 || request.buffer_in_size != 0 || request.buffer_out != 0 ||
|
||||||
|
request.buffer_out_size != 0)
|
||||||
|
{
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||||
|
if (m_devicechange_hook_request)
|
||||||
|
{
|
||||||
|
EnqueueReply(*m_devicechange_hook_request, IPC_SUCCESS);
|
||||||
|
m_devicechange_hook_request.reset();
|
||||||
|
}
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::SuspendResume(USB::Device& device, const IOCtlRequest& request)
|
||||||
|
{
|
||||||
|
const s32 device_id = Memory::Read_U32(request.buffer_in);
|
||||||
|
const s32 resumed = Memory::Read_U32(request.buffer_in + 2 * sizeof(s32));
|
||||||
|
|
||||||
|
// Note: this is unimplemented because there's no easy way to do this in a
|
||||||
|
// platform-independant way (libusb does not support power management).
|
||||||
|
INFO_LOG(IOS_USB, "[%04x:%04x %d] Received %s command", device.GetVid(), device.GetPid(),
|
||||||
|
GetInterfaceNumber(device_id), resumed == 0 ? "suspend" : "resume");
|
||||||
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv)
|
||||||
|
{
|
||||||
|
switch (ioctlv.request)
|
||||||
|
{
|
||||||
|
case USB::IOCTLV_USBV5_CTRLMSG:
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V5CtrlMessage>(ioctlv));
|
||||||
|
case USB::IOCTLV_USBV5_INTRMSG:
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V5IntrMessage>(ioctlv));
|
||||||
|
case USB::IOCTLV_USBV5_BULKMSG:
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V5BulkMessage>(ioctlv));
|
||||||
|
case USB::IOCTLV_USBV5_ISOMSG:
|
||||||
|
return device.SubmitTransfer(std::make_unique<USB::V5IsoMessage>(ioctlv));
|
||||||
|
default:
|
||||||
|
return IPC_EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCCommandResult USB_VEN::HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler)
|
||||||
|
{
|
||||||
|
if (request.buffer_in == 0 || request.buffer_in_size != 0x20)
|
||||||
|
return GetDefaultReply(IPC_EINVAL);
|
||||||
|
|
||||||
|
const s32 device_id = Memory::Read_U32(request.buffer_in);
|
||||||
|
const auto device = GetDeviceByIOSID(device_id);
|
||||||
|
if (!device)
|
||||||
|
return GetDefaultReply(IPC_ENOENT);
|
||||||
|
return handler(this, *device, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_VEN::OnDeviceChange(const ChangeEvent event, std::shared_ptr<USB::Device> device)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> id_map_lock{m_id_map_mutex};
|
||||||
|
if (event == ChangeEvent::Inserted)
|
||||||
|
{
|
||||||
|
for (const auto& interface : device->GetInterfaces(0))
|
||||||
|
{
|
||||||
|
if (interface.bAlternateSetting != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DeviceID id;
|
||||||
|
id.unknown = 0xe7;
|
||||||
|
id.interface_plus_1e = interface.bInterfaceNumber + 0x1e;
|
||||||
|
id.zero = 0x00;
|
||||||
|
id.counter = m_device_number;
|
||||||
|
|
||||||
|
s32 ios_device_id = 0;
|
||||||
|
std::memcpy(&ios_device_id, &id, sizeof(id));
|
||||||
|
ios_device_id = Common::swap32(ios_device_id);
|
||||||
|
m_ios_ids[ios_device_id] = device->GetId();
|
||||||
|
m_device_ids[device->GetId()].insert(ios_device_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event == ChangeEvent::Removed)
|
||||||
|
{
|
||||||
|
for (const s32 ios_id : m_device_ids[device->GetId()])
|
||||||
|
m_ios_ids.erase(ios_id);
|
||||||
|
m_device_ids.erase(device->GetId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_VEN::OnDeviceChangeEnd()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||||
|
TriggerDeviceChangeReply();
|
||||||
|
++m_device_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_VEN::TriggerDeviceChangeReply()
|
||||||
|
{
|
||||||
|
if (!m_devicechange_hook_request)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> id_map_lock{m_id_map_mutex};
|
||||||
|
u8 num_devices = 0;
|
||||||
|
const size_t max_num = m_devicechange_hook_request->buffer_out_size / sizeof(DeviceEntry);
|
||||||
|
for (const auto& ios_device : m_ios_ids)
|
||||||
|
{
|
||||||
|
if (num_devices >= max_num)
|
||||||
|
{
|
||||||
|
WARN_LOG(IOS_USB, "Too many devices (%d ≥ %zu), skipping", num_devices, max_num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s32 ios_device_id = ios_device.first;
|
||||||
|
const auto device = GetDeviceById(m_ios_ids.at(ios_device_id));
|
||||||
|
if (!device)
|
||||||
|
continue;
|
||||||
|
const u8 interface_number = GetInterfaceNumber(ios_device_id);
|
||||||
|
|
||||||
|
// IOS's device list contains entries of the form:
|
||||||
|
// e7 XX 00 YY VV VV PP PP 00 YY DD AA
|
||||||
|
// ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^ ^^^^^ ^^
|
||||||
|
// Device ID VID PID ?? See ID Number of alt settings
|
||||||
|
//
|
||||||
|
// XX is 1e (for a device plugged in to the left port) + DD (interface number).
|
||||||
|
// YY is a counter that starts at 21 and is incremented on every device change.
|
||||||
|
// DD is the interface number (since VEN exposes each interface as a separate device).
|
||||||
|
|
||||||
|
DeviceEntry entry;
|
||||||
|
entry.device_id = Common::swap32(ios_device_id);
|
||||||
|
entry.vid = Common::swap16(device->GetVid());
|
||||||
|
entry.pid = Common::swap16(device->GetPid());
|
||||||
|
entry.unknown = 0x00;
|
||||||
|
entry.device_number = ios_device_id & 0xff;
|
||||||
|
entry.interface_number = interface_number;
|
||||||
|
entry.num_altsettings = device->GetNumberOfAltSettings(interface_number);
|
||||||
|
|
||||||
|
Memory::CopyToEmu(m_devicechange_hook_request->buffer_out + sizeof(entry) * num_devices++,
|
||||||
|
&entry, sizeof(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
EnqueueReply(*m_devicechange_hook_request, num_devices, 0, CoreTiming::FromThread::ANY);
|
||||||
|
m_devicechange_hook_request.reset();
|
||||||
|
INFO_LOG(IOS_USB, "%d device(s), including interfaces", num_devices);
|
||||||
|
}
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
95
Source/Core/Core/IOS/USB/USB_VEN/VEN.h
Normal file
95
Source/Core/Core/IOS/USB/USB_VEN/VEN.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/IOS/Device.h"
|
||||||
|
#include "Core/IOS/IPC.h"
|
||||||
|
#include "Core/IOS/USB/Host.h"
|
||||||
|
|
||||||
|
class PointerWrap;
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace HLE
|
||||||
|
{
|
||||||
|
namespace Device
|
||||||
|
{
|
||||||
|
class USB_VEN final : public USBHost
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
USB_VEN(u32 device_id, const std::string& device_name);
|
||||||
|
~USB_VEN() override;
|
||||||
|
|
||||||
|
ReturnCode Open(const OpenRequest& request) override;
|
||||||
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
|
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||||
|
|
||||||
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct DeviceID
|
||||||
|
{
|
||||||
|
u8 unknown;
|
||||||
|
u8 interface_plus_1e;
|
||||||
|
u8 zero;
|
||||||
|
u8 counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DeviceEntry
|
||||||
|
{
|
||||||
|
s32 device_id;
|
||||||
|
u16 vid;
|
||||||
|
u16 pid;
|
||||||
|
u8 unknown;
|
||||||
|
u8 device_number;
|
||||||
|
u8 interface_number;
|
||||||
|
u8 num_altsettings;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
std::shared_ptr<USB::Device> GetDeviceByIOSID(s32 ios_id) const;
|
||||||
|
u8 GetInterfaceNumber(s32 ios_id) const;
|
||||||
|
|
||||||
|
IPCCommandResult CancelEndpoint(USB::Device& device, const IOCtlRequest& request);
|
||||||
|
IPCCommandResult GetDeviceChange(const IOCtlRequest& request);
|
||||||
|
IPCCommandResult GetDeviceInfo(USB::Device& device, const IOCtlRequest& request);
|
||||||
|
IPCCommandResult SetAlternateSetting(USB::Device& device, const IOCtlRequest& request);
|
||||||
|
IPCCommandResult Shutdown(const IOCtlRequest& request);
|
||||||
|
IPCCommandResult SuspendResume(USB::Device& device, const IOCtlRequest& request);
|
||||||
|
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request);
|
||||||
|
|
||||||
|
using Handler = std::function<IPCCommandResult(USB_VEN*, USB::Device&, const IOCtlRequest&)>;
|
||||||
|
IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler);
|
||||||
|
|
||||||
|
void OnDeviceChange(ChangeEvent, std::shared_ptr<USB::Device>) override;
|
||||||
|
void OnDeviceChangeEnd() override;
|
||||||
|
void TriggerDeviceChangeReply();
|
||||||
|
|
||||||
|
static constexpr u32 VERSION = 0x50001;
|
||||||
|
|
||||||
|
bool m_devicechange_first_call = true;
|
||||||
|
std::mutex m_devicechange_hook_address_mutex;
|
||||||
|
std::unique_ptr<IOCtlRequest> m_devicechange_hook_request;
|
||||||
|
|
||||||
|
mutable std::mutex m_id_map_mutex;
|
||||||
|
u8 m_device_number = 0x21;
|
||||||
|
// IOS device IDs => USB device IDs (one to one)
|
||||||
|
std::map<s32, u64> m_ios_ids;
|
||||||
|
// USB device IDs => IOS device IDs (one to many, because VEN exposes one device per interface)
|
||||||
|
std::map<u64, std::set<s32>> m_device_ids;
|
||||||
|
};
|
||||||
|
} // namespace Device
|
||||||
|
} // namespace HLE
|
||||||
|
} // namespace IOS
|
@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
|||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
static const u32 STATE_VERSION = 73; // Last changed in PR 4651
|
static const u32 STATE_VERSION = 74; // Last changed in PR 4408
|
||||||
|
|
||||||
// Maps savestate versions to Dolphin versions.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
@ -7,6 +7,7 @@ set(GUI_SRCS
|
|||||||
Cheats/CheatsWindow.cpp
|
Cheats/CheatsWindow.cpp
|
||||||
Cheats/CreateCodeDialog.cpp
|
Cheats/CreateCodeDialog.cpp
|
||||||
Cheats/GeckoCodeDiag.cpp
|
Cheats/GeckoCodeDiag.cpp
|
||||||
|
Config/AddUSBDeviceDiag.cpp
|
||||||
Config/AdvancedConfigPane.cpp
|
Config/AdvancedConfigPane.cpp
|
||||||
Config/AudioConfigPane.cpp
|
Config/AudioConfigPane.cpp
|
||||||
Config/ConfigMain.cpp
|
Config/ConfigMain.cpp
|
||||||
|
164
Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp
Normal file
164
Source/Core/DolphinWX/Config/AddUSBDeviceDiag.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright 2016 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
#include <wx/listbox.h>
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
#include <wx/textctrl.h>
|
||||||
|
#include <wx/window.h>
|
||||||
|
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
|
#include "Core/ConfigManager.h"
|
||||||
|
#include "DolphinWX/Config/AddUSBDeviceDiag.h"
|
||||||
|
#include "DolphinWX/WxUtils.h"
|
||||||
|
#include "UICommon/USBUtils.h"
|
||||||
|
|
||||||
|
AddUSBDeviceDiag::AddUSBDeviceDiag(wxWindow* const parent)
|
||||||
|
: wxDialog(parent, wxID_ANY, _("Add New USB Device"))
|
||||||
|
{
|
||||||
|
InitControls();
|
||||||
|
|
||||||
|
RefreshDeviceList();
|
||||||
|
Bind(wxEVT_TIMER, &AddUSBDeviceDiag::OnRefreshDevicesTimer, this,
|
||||||
|
m_refresh_devices_timer.GetId());
|
||||||
|
m_refresh_devices_timer.Start(DEVICE_REFRESH_INTERVAL_MS, wxTIMER_CONTINUOUS);
|
||||||
|
|
||||||
|
auto* const btn_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
||||||
|
btn_sizer->GetAffirmativeButton()->SetLabel(_("Add"));
|
||||||
|
Bind(wxEVT_BUTTON, &AddUSBDeviceDiag::OnSave, this, wxID_OK);
|
||||||
|
|
||||||
|
auto* const sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
const int space5 = FromDIP(5);
|
||||||
|
sizer->AddSpacer(FromDIP(10));
|
||||||
|
sizer->Add(new wxStaticText(this, wxID_ANY, _("Enter USB device ID"), wxDefaultPosition,
|
||||||
|
wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL),
|
||||||
|
0, wxEXPAND | wxBOTTOM, FromDIP(10));
|
||||||
|
sizer->Add(CreateManualControlsSizer(), 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
|
sizer->Add(new wxStaticText(this, wxID_ANY, _("or select a device"), wxDefaultPosition,
|
||||||
|
wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL),
|
||||||
|
0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10));
|
||||||
|
auto* const device_list_sizer = CreateDeviceListSizer();
|
||||||
|
sizer->Add(device_list_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, space5);
|
||||||
|
sizer->SetItemMinSize(device_list_sizer, FromDIP(350), FromDIP(150));
|
||||||
|
sizer->Add(btn_sizer, 0, wxEXPAND);
|
||||||
|
sizer->AddSpacer(space5);
|
||||||
|
|
||||||
|
SetSizerAndFit(sizer);
|
||||||
|
Center();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUSBDeviceDiag::InitControls()
|
||||||
|
{
|
||||||
|
m_new_device_vid_ctrl = new wxTextCtrl(this, wxID_ANY);
|
||||||
|
m_new_device_pid_ctrl = new wxTextCtrl(this, wxID_ANY);
|
||||||
|
// i18n: VID means Vendor ID (in the context of a USB device)
|
||||||
|
m_new_device_vid_ctrl->SetHint(_("Device VID (e.g., 057e)"));
|
||||||
|
// i18n: PID means Product ID (in the context of a USB device), not Process ID
|
||||||
|
m_new_device_pid_ctrl->SetHint(_("Device PID (e.g., 0305)"));
|
||||||
|
|
||||||
|
m_inserted_devices_listbox = new wxListBox(this, wxID_ANY);
|
||||||
|
m_inserted_devices_listbox->Bind(wxEVT_LISTBOX, &AddUSBDeviceDiag::OnDeviceSelection, this);
|
||||||
|
m_inserted_devices_listbox->Bind(wxEVT_LISTBOX_DCLICK, &AddUSBDeviceDiag::OnSave, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUSBDeviceDiag::RefreshDeviceList()
|
||||||
|
{
|
||||||
|
const auto& current_devices = USBUtils::GetInsertedDevices();
|
||||||
|
if (current_devices == m_shown_devices)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_inserted_devices_listbox->Freeze();
|
||||||
|
const auto selection_string = m_inserted_devices_listbox->GetStringSelection();
|
||||||
|
m_inserted_devices_listbox->Clear();
|
||||||
|
for (const auto& device : current_devices)
|
||||||
|
{
|
||||||
|
if (SConfig::GetInstance().IsUSBDeviceWhitelisted(device.first))
|
||||||
|
continue;
|
||||||
|
m_inserted_devices_listbox->Append(device.second, new USBPassthroughDeviceEntry(device.first));
|
||||||
|
}
|
||||||
|
if (!selection_string.empty())
|
||||||
|
m_inserted_devices_listbox->SetStringSelection(selection_string);
|
||||||
|
m_inserted_devices_listbox->Thaw();
|
||||||
|
|
||||||
|
m_shown_devices = current_devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxSizer* AddUSBDeviceDiag::CreateManualControlsSizer()
|
||||||
|
{
|
||||||
|
const int space5 = FromDIP(5);
|
||||||
|
auto* const sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
sizer->Add(m_new_device_vid_ctrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
|
sizer->Add(m_new_device_pid_ctrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
|
|
||||||
|
return sizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxSizer* AddUSBDeviceDiag::CreateDeviceListSizer()
|
||||||
|
{
|
||||||
|
const int space5 = FromDIP(5);
|
||||||
|
auto* const sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
sizer->Add(m_inserted_devices_listbox, 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
|
|
||||||
|
return sizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsValidUSBIDString(const std::string& string)
|
||||||
|
{
|
||||||
|
if (string.empty() || string.length() > 4)
|
||||||
|
return false;
|
||||||
|
return std::all_of(string.begin(), string.end(),
|
||||||
|
[](const auto character) { return std::isxdigit(character) != 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUSBDeviceDiag::OnRefreshDevicesTimer(wxTimerEvent&)
|
||||||
|
{
|
||||||
|
RefreshDeviceList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUSBDeviceDiag::OnDeviceSelection(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
const int index = m_inserted_devices_listbox->GetSelection();
|
||||||
|
if (index == wxNOT_FOUND)
|
||||||
|
return;
|
||||||
|
auto* const entry = static_cast<const USBPassthroughDeviceEntry*>(
|
||||||
|
m_inserted_devices_listbox->GetClientObject(index));
|
||||||
|
m_new_device_vid_ctrl->SetValue(StringFromFormat("%04x", entry->m_vid));
|
||||||
|
m_new_device_pid_ctrl->SetValue(StringFromFormat("%04x", entry->m_pid));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUSBDeviceDiag::OnSave(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
const std::string vid_string = StripSpaces(WxStrToStr(m_new_device_vid_ctrl->GetValue()));
|
||||||
|
const std::string pid_string = StripSpaces(WxStrToStr(m_new_device_pid_ctrl->GetValue()));
|
||||||
|
if (!IsValidUSBIDString(vid_string))
|
||||||
|
{
|
||||||
|
// i18n: Here, VID means Vendor ID (for a USB device).
|
||||||
|
WxUtils::ShowErrorDialog(_("The entered VID is invalid."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!IsValidUSBIDString(pid_string))
|
||||||
|
{
|
||||||
|
// i18n: Here, PID means Product ID (for a USB device).
|
||||||
|
WxUtils::ShowErrorDialog(_("The entered PID is invalid."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u16 vid = static_cast<u16>(std::stoul(vid_string, nullptr, 16));
|
||||||
|
const u16 pid = static_cast<u16>(std::stoul(pid_string, nullptr, 16));
|
||||||
|
|
||||||
|
if (SConfig::GetInstance().IsUSBDeviceWhitelisted({vid, pid}))
|
||||||
|
{
|
||||||
|
WxUtils::ShowErrorDialog(_("This USB device is already whitelisted."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SConfig::GetInstance().m_usb_passthrough_devices.emplace(vid, pid);
|
||||||
|
AcceptAndClose();
|
||||||
|
}
|
56
Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h
Normal file
56
Source/Core/DolphinWX/Config/AddUSBDeviceDiag.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2016 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <wx/clntdata.h>
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
#include <wx/timer.h>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
class wxListBox;
|
||||||
|
class wxSizer;
|
||||||
|
class wxTextCtrl;
|
||||||
|
|
||||||
|
class USBPassthroughDeviceEntry final : public wxClientData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit USBPassthroughDeviceEntry(const std::pair<u16, u16> pair)
|
||||||
|
: m_vid(pair.first), m_pid(pair.second)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
const u16 m_vid;
|
||||||
|
const u16 m_pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This dialog is used to add a new USB device to the USB passthrough whitelist,
|
||||||
|
// either by selecting a connected USB device or by entering the PID/VID manually.
|
||||||
|
class AddUSBDeviceDiag final : public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AddUSBDeviceDiag(wxWindow* parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int DEVICE_REFRESH_INTERVAL_MS = 100;
|
||||||
|
|
||||||
|
void InitControls();
|
||||||
|
void RefreshDeviceList();
|
||||||
|
wxSizer* CreateManualControlsSizer();
|
||||||
|
wxSizer* CreateDeviceListSizer();
|
||||||
|
|
||||||
|
void OnRefreshDevicesTimer(wxTimerEvent&);
|
||||||
|
void OnDeviceSelection(wxCommandEvent&);
|
||||||
|
void OnSave(wxCommandEvent&);
|
||||||
|
|
||||||
|
std::map<std::pair<u16, u16>, std::string> m_shown_devices;
|
||||||
|
wxTimer m_refresh_devices_timer{this};
|
||||||
|
|
||||||
|
wxTextCtrl* m_new_device_vid_ctrl;
|
||||||
|
wxTextCtrl* m_new_device_pid_ctrl;
|
||||||
|
wxListBox* m_inserted_devices_listbox;
|
||||||
|
};
|
@ -2,11 +2,11 @@
|
|||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "DolphinWX/Config/WiiConfigPane.h"
|
#include <wx/button.h>
|
||||||
|
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
#include <wx/choice.h>
|
#include <wx/choice.h>
|
||||||
#include <wx/gbsizer.h>
|
#include <wx/gbsizer.h>
|
||||||
|
#include <wx/listbox.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/slider.h>
|
#include <wx/slider.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
@ -14,9 +14,12 @@
|
|||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/IOS/IPC.h"
|
#include "Core/IOS/IPC.h"
|
||||||
|
#include "DolphinWX/Config/AddUSBDeviceDiag.h"
|
||||||
|
#include "DolphinWX/Config/WiiConfigPane.h"
|
||||||
#include "DolphinWX/DolphinSlider.h"
|
#include "DolphinWX/DolphinSlider.h"
|
||||||
#include "DolphinWX/WxEventUtils.h"
|
#include "DolphinWX/WxEventUtils.h"
|
||||||
#include "DolphinWX/WxUtils.h"
|
#include "DolphinWX/WxUtils.h"
|
||||||
|
#include "UICommon/USBUtils.h"
|
||||||
|
|
||||||
WiiConfigPane::WiiConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id)
|
WiiConfigPane::WiiConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id)
|
||||||
{
|
{
|
||||||
@ -52,6 +55,10 @@ void WiiConfigPane::InitializeGUI()
|
|||||||
new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_system_language_strings);
|
new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_system_language_strings);
|
||||||
m_sd_card_checkbox = new wxCheckBox(this, wxID_ANY, _("Insert SD Card"));
|
m_sd_card_checkbox = new wxCheckBox(this, wxID_ANY, _("Insert SD Card"));
|
||||||
m_connect_keyboard_checkbox = new wxCheckBox(this, wxID_ANY, _("Connect USB Keyboard"));
|
m_connect_keyboard_checkbox = new wxCheckBox(this, wxID_ANY, _("Connect USB Keyboard"));
|
||||||
|
m_usb_passthrough_devices_listbox =
|
||||||
|
new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 100));
|
||||||
|
m_usb_passthrough_add_device_btn = new wxButton(this, wxID_ANY, _("Add..."));
|
||||||
|
m_usb_passthrough_rem_device_btn = new wxButton(this, wxID_ANY, _("Remove"));
|
||||||
m_bt_sensor_bar_pos =
|
m_bt_sensor_bar_pos =
|
||||||
new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_bt_sensor_bar_pos_strings);
|
new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_bt_sensor_bar_pos_strings);
|
||||||
m_bt_sensor_bar_sens = new DolphinSlider(this, wxID_ANY, 0, 0, 4);
|
m_bt_sensor_bar_sens = new DolphinSlider(this, wxID_ANY, 0, 0, 4);
|
||||||
@ -79,6 +86,12 @@ void WiiConfigPane::InitializeGUI()
|
|||||||
misc_settings_grid_sizer->Add(m_system_language_choice, wxGBPosition(3, 1), wxDefaultSpan,
|
misc_settings_grid_sizer->Add(m_system_language_choice, wxGBPosition(3, 1), wxDefaultSpan,
|
||||||
wxALIGN_CENTER_VERTICAL);
|
wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
|
auto* const usb_passthrough_btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
usb_passthrough_btn_sizer->AddStretchSpacer();
|
||||||
|
usb_passthrough_btn_sizer->Add(m_usb_passthrough_add_device_btn, 0,
|
||||||
|
wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, space5);
|
||||||
|
usb_passthrough_btn_sizer->Add(m_usb_passthrough_rem_device_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
auto* const bt_sensor_bar_pos_sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto* const bt_sensor_bar_pos_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
bt_sensor_bar_pos_sizer->Add(new wxStaticText(this, wxID_ANY, _("Min")), 0,
|
bt_sensor_bar_pos_sizer->Add(new wxStaticText(this, wxID_ANY, _("Min")), 0,
|
||||||
wxALIGN_CENTER_VERTICAL);
|
wxALIGN_CENTER_VERTICAL);
|
||||||
@ -123,6 +136,15 @@ void WiiConfigPane::InitializeGUI()
|
|||||||
device_settings_sizer->Add(m_connect_keyboard_checkbox, 0, wxLEFT | wxRIGHT, space5);
|
device_settings_sizer->Add(m_connect_keyboard_checkbox, 0, wxLEFT | wxRIGHT, space5);
|
||||||
device_settings_sizer->AddSpacer(space5);
|
device_settings_sizer->AddSpacer(space5);
|
||||||
|
|
||||||
|
auto* const usb_passthrough_sizer =
|
||||||
|
new wxStaticBoxSizer(wxVERTICAL, this, _("Whitelisted USB Passthrough Devices"));
|
||||||
|
usb_passthrough_sizer->AddSpacer(space5);
|
||||||
|
usb_passthrough_sizer->Add(m_usb_passthrough_devices_listbox, 0, wxEXPAND | wxLEFT | wxRIGHT,
|
||||||
|
space5);
|
||||||
|
usb_passthrough_sizer->AddSpacer(space5);
|
||||||
|
usb_passthrough_sizer->Add(usb_passthrough_btn_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
|
usb_passthrough_sizer->AddSpacer(space5);
|
||||||
|
|
||||||
auto* const bt_settings_static_sizer =
|
auto* const bt_settings_static_sizer =
|
||||||
new wxStaticBoxSizer(wxVERTICAL, this, _("Wii Remote Settings"));
|
new wxStaticBoxSizer(wxVERTICAL, this, _("Wii Remote Settings"));
|
||||||
bt_settings_static_sizer->AddSpacer(space5);
|
bt_settings_static_sizer->AddSpacer(space5);
|
||||||
@ -135,6 +157,8 @@ void WiiConfigPane::InitializeGUI()
|
|||||||
main_sizer->AddSpacer(space5);
|
main_sizer->AddSpacer(space5);
|
||||||
main_sizer->Add(device_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
main_sizer->Add(device_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
main_sizer->AddSpacer(space5);
|
main_sizer->AddSpacer(space5);
|
||||||
|
main_sizer->Add(usb_passthrough_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
|
main_sizer->AddSpacer(space5);
|
||||||
main_sizer->Add(bt_settings_static_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
main_sizer->Add(bt_settings_static_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
|
||||||
main_sizer->AddSpacer(space5);
|
main_sizer->AddSpacer(space5);
|
||||||
|
|
||||||
@ -151,12 +175,26 @@ void WiiConfigPane::LoadGUIValues()
|
|||||||
m_sd_card_checkbox->SetValue(SConfig::GetInstance().m_WiiSDCard);
|
m_sd_card_checkbox->SetValue(SConfig::GetInstance().m_WiiSDCard);
|
||||||
m_connect_keyboard_checkbox->SetValue(SConfig::GetInstance().m_WiiKeyboard);
|
m_connect_keyboard_checkbox->SetValue(SConfig::GetInstance().m_WiiKeyboard);
|
||||||
|
|
||||||
|
PopulateUSBPassthroughListbox();
|
||||||
|
|
||||||
m_bt_sensor_bar_pos->SetSelection(SConfig::GetInstance().m_sensor_bar_position);
|
m_bt_sensor_bar_pos->SetSelection(SConfig::GetInstance().m_sensor_bar_position);
|
||||||
m_bt_sensor_bar_sens->SetValue(SConfig::GetInstance().m_sensor_bar_sensitivity);
|
m_bt_sensor_bar_sens->SetValue(SConfig::GetInstance().m_sensor_bar_sensitivity);
|
||||||
m_bt_speaker_volume->SetValue(SConfig::GetInstance().m_speaker_volume);
|
m_bt_speaker_volume->SetValue(SConfig::GetInstance().m_speaker_volume);
|
||||||
m_bt_wiimote_motor->SetValue(SConfig::GetInstance().m_wiimote_motor);
|
m_bt_wiimote_motor->SetValue(SConfig::GetInstance().m_wiimote_motor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WiiConfigPane::PopulateUSBPassthroughListbox()
|
||||||
|
{
|
||||||
|
m_usb_passthrough_devices_listbox->Freeze();
|
||||||
|
m_usb_passthrough_devices_listbox->Clear();
|
||||||
|
for (const auto& device : SConfig::GetInstance().m_usb_passthrough_devices)
|
||||||
|
{
|
||||||
|
m_usb_passthrough_devices_listbox->Append(USBUtils::GetDeviceName(device),
|
||||||
|
new USBPassthroughDeviceEntry(device));
|
||||||
|
}
|
||||||
|
m_usb_passthrough_devices_listbox->Thaw();
|
||||||
|
}
|
||||||
|
|
||||||
void WiiConfigPane::BindEvents()
|
void WiiConfigPane::BindEvents()
|
||||||
{
|
{
|
||||||
m_screensaver_checkbox->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnScreenSaverCheckBoxChanged, this);
|
m_screensaver_checkbox->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnScreenSaverCheckBoxChanged, this);
|
||||||
@ -186,6 +224,38 @@ void WiiConfigPane::BindEvents()
|
|||||||
|
|
||||||
m_bt_wiimote_motor->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnWiimoteMotorChanged, this);
|
m_bt_wiimote_motor->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnWiimoteMotorChanged, this);
|
||||||
m_bt_wiimote_motor->Bind(wxEVT_UPDATE_UI, &WxEventUtils::OnEnableIfCoreNotRunning);
|
m_bt_wiimote_motor->Bind(wxEVT_UPDATE_UI, &WxEventUtils::OnEnableIfCoreNotRunning);
|
||||||
|
|
||||||
|
m_usb_passthrough_add_device_btn->Bind(wxEVT_BUTTON, &WiiConfigPane::OnUSBWhitelistAddButton,
|
||||||
|
this);
|
||||||
|
|
||||||
|
m_usb_passthrough_rem_device_btn->Bind(wxEVT_BUTTON, &WiiConfigPane::OnUSBWhitelistRemoveButton,
|
||||||
|
this);
|
||||||
|
m_usb_passthrough_rem_device_btn->Bind(wxEVT_UPDATE_UI,
|
||||||
|
&WiiConfigPane::OnUSBWhitelistRemoveButtonUpdate, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiiConfigPane::OnUSBWhitelistAddButton(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
AddUSBDeviceDiag add_dialog{this};
|
||||||
|
// Reload the USB device whitelist
|
||||||
|
if (add_dialog.ShowModal() == wxID_OK)
|
||||||
|
PopulateUSBPassthroughListbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiiConfigPane::OnUSBWhitelistRemoveButton(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
const int index = m_usb_passthrough_devices_listbox->GetSelection();
|
||||||
|
if (index == wxNOT_FOUND)
|
||||||
|
return;
|
||||||
|
auto* const entry = static_cast<const USBPassthroughDeviceEntry*>(
|
||||||
|
m_usb_passthrough_devices_listbox->GetClientObject(index));
|
||||||
|
SConfig::GetInstance().m_usb_passthrough_devices.erase({entry->m_vid, entry->m_pid});
|
||||||
|
m_usb_passthrough_devices_listbox->Delete(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiiConfigPane::OnUSBWhitelistRemoveButtonUpdate(wxUpdateUIEvent& event)
|
||||||
|
{
|
||||||
|
event.Enable(m_usb_passthrough_devices_listbox->GetSelection() != wxNOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiiConfigPane::OnScreenSaverCheckBoxChanged(wxCommandEvent& event)
|
void WiiConfigPane::OnScreenSaverCheckBoxChanged(wxCommandEvent& event)
|
||||||
|
@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
#include <wx/arrstr.h>
|
#include <wx/arrstr.h>
|
||||||
#include <wx/panel.h>
|
#include <wx/panel.h>
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
|
|
||||||
class DolphinSlider;
|
class DolphinSlider;
|
||||||
|
class wxButton;
|
||||||
class wxCheckBox;
|
class wxCheckBox;
|
||||||
class wxChoice;
|
class wxChoice;
|
||||||
|
class wxListBox;
|
||||||
class wxSlider;
|
class wxSlider;
|
||||||
|
|
||||||
class WiiConfigPane final : public wxPanel
|
class WiiConfigPane final : public wxPanel
|
||||||
@ -23,6 +24,8 @@ private:
|
|||||||
void LoadGUIValues();
|
void LoadGUIValues();
|
||||||
void BindEvents();
|
void BindEvents();
|
||||||
|
|
||||||
|
void PopulateUSBPassthroughListbox();
|
||||||
|
|
||||||
void OnScreenSaverCheckBoxChanged(wxCommandEvent&);
|
void OnScreenSaverCheckBoxChanged(wxCommandEvent&);
|
||||||
void OnPAL60CheckBoxChanged(wxCommandEvent&);
|
void OnPAL60CheckBoxChanged(wxCommandEvent&);
|
||||||
void OnSDCardCheckBoxChanged(wxCommandEvent&);
|
void OnSDCardCheckBoxChanged(wxCommandEvent&);
|
||||||
@ -30,6 +33,10 @@ private:
|
|||||||
void OnSystemLanguageChoiceChanged(wxCommandEvent&);
|
void OnSystemLanguageChoiceChanged(wxCommandEvent&);
|
||||||
void OnAspectRatioChoiceChanged(wxCommandEvent&);
|
void OnAspectRatioChoiceChanged(wxCommandEvent&);
|
||||||
|
|
||||||
|
void OnUSBWhitelistAddButton(wxCommandEvent&);
|
||||||
|
void OnUSBWhitelistRemoveButton(wxCommandEvent&);
|
||||||
|
void OnUSBWhitelistRemoveButtonUpdate(wxUpdateUIEvent&);
|
||||||
|
|
||||||
void OnSensorBarPosChanged(wxCommandEvent&);
|
void OnSensorBarPosChanged(wxCommandEvent&);
|
||||||
void OnSensorBarSensChanged(wxCommandEvent&);
|
void OnSensorBarSensChanged(wxCommandEvent&);
|
||||||
void OnSpeakerVolumeChanged(wxCommandEvent&);
|
void OnSpeakerVolumeChanged(wxCommandEvent&);
|
||||||
@ -46,6 +53,10 @@ private:
|
|||||||
wxChoice* m_system_language_choice;
|
wxChoice* m_system_language_choice;
|
||||||
wxChoice* m_aspect_ratio_choice;
|
wxChoice* m_aspect_ratio_choice;
|
||||||
|
|
||||||
|
wxListBox* m_usb_passthrough_devices_listbox;
|
||||||
|
wxButton* m_usb_passthrough_add_device_btn;
|
||||||
|
wxButton* m_usb_passthrough_rem_device_btn;
|
||||||
|
|
||||||
wxChoice* m_bt_sensor_bar_pos;
|
wxChoice* m_bt_sensor_bar_pos;
|
||||||
DolphinSlider* m_bt_sensor_bar_sens;
|
DolphinSlider* m_bt_sensor_bar_sens;
|
||||||
DolphinSlider* m_bt_speaker_volume;
|
DolphinSlider* m_bt_speaker_volume;
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
<ClCompile Include="Cheats\CheatsWindow.cpp" />
|
<ClCompile Include="Cheats\CheatsWindow.cpp" />
|
||||||
<ClCompile Include="Cheats\CreateCodeDialog.cpp" />
|
<ClCompile Include="Cheats\CreateCodeDialog.cpp" />
|
||||||
<ClCompile Include="Cheats\GeckoCodeDiag.cpp" />
|
<ClCompile Include="Cheats\GeckoCodeDiag.cpp" />
|
||||||
|
<ClCompile Include="Config\AddUSBDeviceDiag.cpp" />
|
||||||
<ClCompile Include="Config\AdvancedConfigPane.cpp" />
|
<ClCompile Include="Config\AdvancedConfigPane.cpp" />
|
||||||
<ClCompile Include="Config\AudioConfigPane.cpp" />
|
<ClCompile Include="Config\AudioConfigPane.cpp" />
|
||||||
<ClCompile Include="Config\ConfigMain.cpp" />
|
<ClCompile Include="Config\ConfigMain.cpp" />
|
||||||
@ -139,6 +140,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Cheats\ActionReplayCodesPanel.h" />
|
<ClInclude Include="Cheats\ActionReplayCodesPanel.h" />
|
||||||
<ClInclude Include="Cheats\ARCodeAddEdit.h" />
|
<ClInclude Include="Cheats\ARCodeAddEdit.h" />
|
||||||
|
<ClInclude Include="Config\AddUSBDeviceDiag.h" />
|
||||||
<ClInclude Include="Config\AdvancedConfigPane.h" />
|
<ClInclude Include="Config\AdvancedConfigPane.h" />
|
||||||
<ClInclude Include="Config\AudioConfigPane.h" />
|
<ClInclude Include="Config\AudioConfigPane.h" />
|
||||||
<ClInclude Include="Config\GameCubeConfigPane.h" />
|
<ClInclude Include="Config\GameCubeConfigPane.h" />
|
||||||
|
@ -224,6 +224,9 @@
|
|||||||
<ClCompile Include="Config\PathConfigPane.cpp">
|
<ClCompile Include="Config\PathConfigPane.cpp">
|
||||||
<Filter>GUI\Config</Filter>
|
<Filter>GUI\Config</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Config\AddUSBDeviceDiag.cpp">
|
||||||
|
<Filter>GUI\Config</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="Config\AdvancedConfigPane.cpp">
|
<ClCompile Include="Config\AdvancedConfigPane.cpp">
|
||||||
<Filter>GUI\Config</Filter>
|
<Filter>GUI\Config</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -443,6 +446,9 @@
|
|||||||
<ClInclude Include="Config\PathConfigPane.h">
|
<ClInclude Include="Config\PathConfigPane.h">
|
||||||
<Filter>GUI\Config</Filter>
|
<Filter>GUI\Config</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="Config\AddUSBDeviceDiag.h">
|
||||||
|
<Filter>GUI\Config</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="Config\AdvancedConfigPane.h">
|
<ClInclude Include="Config\AdvancedConfigPane.h">
|
||||||
<Filter>GUI\Config</Filter>
|
<Filter>GUI\Config</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "Common/Flag.h"
|
#include "Common/Flag.h"
|
||||||
|
#include "Common/LibusbContext.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/Thread.h"
|
#include "Common/Thread.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
@ -50,7 +52,7 @@ static Common::Flag s_adapter_detect_thread_running;
|
|||||||
static std::function<void(void)> s_detect_callback;
|
static std::function<void(void)> s_detect_callback;
|
||||||
|
|
||||||
static bool s_libusb_driver_not_supported = false;
|
static bool s_libusb_driver_not_supported = false;
|
||||||
static libusb_context* s_libusb_context = nullptr;
|
static std::shared_ptr<libusb_context> s_libusb_context;
|
||||||
#if defined(__FreeBSD__) && __FreeBSD__ >= 11
|
#if defined(__FreeBSD__) && __FreeBSD__ >= 11
|
||||||
static bool s_libusb_hotplug_enabled = true;
|
static bool s_libusb_hotplug_enabled = true;
|
||||||
#else
|
#else
|
||||||
@ -116,8 +118,8 @@ static void ScanThreadFunc()
|
|||||||
if (s_libusb_hotplug_enabled)
|
if (s_libusb_hotplug_enabled)
|
||||||
{
|
{
|
||||||
if (libusb_hotplug_register_callback(
|
if (libusb_hotplug_register_callback(
|
||||||
s_libusb_context, (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
|
s_libusb_context.get(), (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
|
||||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
|
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
|
||||||
LIBUSB_HOTPLUG_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback,
|
LIBUSB_HOTPLUG_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback,
|
||||||
nullptr, &s_hotplug_handle) != LIBUSB_SUCCESS)
|
nullptr, &s_hotplug_handle) != LIBUSB_SUCCESS)
|
||||||
s_libusb_hotplug_enabled = false;
|
s_libusb_hotplug_enabled = false;
|
||||||
@ -131,7 +133,7 @@ static void ScanThreadFunc()
|
|||||||
if (s_libusb_hotplug_enabled)
|
if (s_libusb_hotplug_enabled)
|
||||||
{
|
{
|
||||||
static timeval tv = {0, 500000};
|
static timeval tv = {0, 500000};
|
||||||
libusb_handle_events_timeout(s_libusb_context, &tv);
|
libusb_handle_events_timeout(s_libusb_context.get(), &tv);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -168,11 +170,9 @@ void Init()
|
|||||||
|
|
||||||
s_libusb_driver_not_supported = false;
|
s_libusb_driver_not_supported = false;
|
||||||
|
|
||||||
int ret = libusb_init(&s_libusb_context);
|
s_libusb_context = LibusbContext::Get();
|
||||||
|
if (!s_libusb_context)
|
||||||
if (ret)
|
|
||||||
{
|
{
|
||||||
ERROR_LOG(SERIALINTERFACE, "libusb_init failed with error: %d", ret);
|
|
||||||
s_libusb_driver_not_supported = true;
|
s_libusb_driver_not_supported = true;
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ void StopScanThread()
|
|||||||
static void Setup()
|
static void Setup()
|
||||||
{
|
{
|
||||||
libusb_device** list;
|
libusb_device** list;
|
||||||
ssize_t cnt = libusb_get_device_list(s_libusb_context, &list);
|
ssize_t cnt = libusb_get_device_list(s_libusb_context.get(), &list);
|
||||||
|
|
||||||
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
||||||
{
|
{
|
||||||
@ -335,16 +335,11 @@ void Shutdown()
|
|||||||
StopScanThread();
|
StopScanThread();
|
||||||
#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102
|
#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102
|
||||||
if (s_libusb_hotplug_enabled)
|
if (s_libusb_hotplug_enabled)
|
||||||
libusb_hotplug_deregister_callback(s_libusb_context, s_hotplug_handle);
|
libusb_hotplug_deregister_callback(s_libusb_context.get(), s_hotplug_handle);
|
||||||
#endif
|
#endif
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
if (s_libusb_context)
|
s_libusb_context.reset();
|
||||||
{
|
|
||||||
libusb_exit(s_libusb_context);
|
|
||||||
s_libusb_context = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_libusb_driver_not_supported = false;
|
s_libusb_driver_not_supported = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
set(SRCS Disassembler.cpp
|
set(SRCS Disassembler.cpp
|
||||||
UICommon.cpp)
|
UICommon.cpp
|
||||||
|
USBUtils.cpp)
|
||||||
|
|
||||||
set(LIBS common)
|
set(LIBS common)
|
||||||
|
if(LIBUSB_FOUND)
|
||||||
|
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_dolphin_library(uicommon "${SRCS}" "${LIBS}")
|
add_dolphin_library(uicommon "${SRCS}" "${LIBS}")
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "InputCommon/GCAdapter.h"
|
#include "InputCommon/GCAdapter.h"
|
||||||
|
|
||||||
#include "UICommon/UICommon.h"
|
#include "UICommon/UICommon.h"
|
||||||
|
#include "UICommon/USBUtils.h"
|
||||||
|
|
||||||
#include "VideoCommon/VideoBackendBase.h"
|
#include "VideoCommon/VideoBackendBase.h"
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ void Init()
|
|||||||
VideoBackendBase::PopulateList();
|
VideoBackendBase::PopulateList();
|
||||||
WiimoteReal::LoadSettings();
|
WiimoteReal::LoadSettings();
|
||||||
GCAdapter::Init();
|
GCAdapter::Init();
|
||||||
|
USBUtils::Init();
|
||||||
VideoBackendBase::ActivateBackend(SConfig::GetInstance().m_strVideoBackend);
|
VideoBackendBase::ActivateBackend(SConfig::GetInstance().m_strVideoBackend);
|
||||||
|
|
||||||
SetEnableAlert(SConfig::GetInstance().bUsePanicHandlers);
|
SetEnableAlert(SConfig::GetInstance().bUsePanicHandlers);
|
||||||
@ -40,6 +42,7 @@ void Shutdown()
|
|||||||
WiimoteReal::Shutdown();
|
WiimoteReal::Shutdown();
|
||||||
VideoBackendBase::ClearList();
|
VideoBackendBase::ClearList();
|
||||||
SConfig::Shutdown();
|
SConfig::Shutdown();
|
||||||
|
USBUtils::Shutdown();
|
||||||
LogManager::Shutdown();
|
LogManager::Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,10 +45,14 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="UICommon.cpp" />
|
<ClCompile Include="UICommon.cpp" />
|
||||||
<ClCompile Include="Disassembler.cpp" />
|
<ClCompile Include="Disassembler.cpp" />
|
||||||
|
<ClCompile Include="USBUtils.cpp">
|
||||||
|
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="UICommon.h" />
|
<ClInclude Include="UICommon.h" />
|
||||||
<ClInclude Include="Disassembler.h" />
|
<ClInclude Include="Disassembler.h" />
|
||||||
|
<ClInclude Include="USBUtils.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
82
Source/Core/UICommon/USBUtils.cpp
Normal file
82
Source/Core/UICommon/USBUtils.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
#include <libusb.h>
|
||||||
|
#include "Common/LibusbContext.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
|
#include "UICommon/USBUtils.h"
|
||||||
|
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
static std::shared_ptr<libusb_context> s_libusb_context;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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::pair<u16, u16>, std::string> s_wii_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)"},
|
||||||
|
{{0x1430, 0x0100}, "Tony Hawk Ride Skateboard"},
|
||||||
|
{{0x1430, 0x0150}, "Skylanders Portal"},
|
||||||
|
{{0x1bad, 0x0004}, "Harmonix Guitar Controller"},
|
||||||
|
{{0x1bad, 0x3110}, "Rock Band 3 Mustang Guitar Dongle"},
|
||||||
|
{{0x1bad, 0x3430}, "Rock Band Drum Set"},
|
||||||
|
{{0x21a4, 0xac40}, "EA Active NFL"},
|
||||||
|
}};
|
||||||
|
|
||||||
|
namespace USBUtils
|
||||||
|
{
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
s_libusb_context = LibusbContext::Get();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
s_libusb_context = nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::pair<u16, u16>, std::string> GetInsertedDevices()
|
||||||
|
{
|
||||||
|
std::map<std::pair<u16, u16>, std::string> devices;
|
||||||
|
|
||||||
|
#ifdef __LIBUSB__
|
||||||
|
if (!s_libusb_context)
|
||||||
|
return devices;
|
||||||
|
|
||||||
|
libusb_device** list;
|
||||||
|
const ssize_t cnt = libusb_get_device_list(s_libusb_context.get(), &list);
|
||||||
|
for (ssize_t i = 0; i < cnt; ++i)
|
||||||
|
{
|
||||||
|
libusb_device_descriptor descr;
|
||||||
|
libusb_get_device_descriptor(list[i], &descr);
|
||||||
|
const std::pair<u16, u16> vid_pid{descr.idVendor, descr.idProduct};
|
||||||
|
devices[vid_pid] = GetDeviceName(vid_pid);
|
||||||
|
}
|
||||||
|
libusb_free_device_list(list, 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetDeviceName(const std::pair<u16, u16> vid_pid)
|
||||||
|
{
|
||||||
|
const auto iter = s_wii_peripherals.find(vid_pid);
|
||||||
|
const std::string device_name = iter == s_wii_peripherals.cend() ? "Unknown" : iter->second;
|
||||||
|
return StringFromFormat("%04x:%04x - %s", vid_pid.first, vid_pid.second, device_name.c_str());
|
||||||
|
}
|
||||||
|
} // namespace USBUtils
|
20
Source/Core/UICommon/USBUtils.h
Normal file
20
Source/Core/UICommon/USBUtils.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
namespace USBUtils
|
||||||
|
{
|
||||||
|
void Init();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
std::map<std::pair<u16, u16>, std::string> GetInsertedDevices();
|
||||||
|
std::string GetDeviceName(std::pair<u16, u16> vid_pid);
|
||||||
|
} // namespace USBUtils
|
@ -51,7 +51,7 @@
|
|||||||
<AdditionalIncludeDirectories>$(ExternalsDir)xxhash;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ExternalsDir)xxhash;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories>$(ExternalsDir)zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ExternalsDir)zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions>USE_UPNP;__LIBUSB__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>USE_UPNP;USE_USBDK;__LIBUSB__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions>PSAPI_VERSION=1;_M_X86=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>PSAPI_VERSION=1;_M_X86=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions>SFML_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>SFML_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions>CURL_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>CURL_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user