Merge pull request #4408 from leoetlino/usb

IOS: USB support (OH0, USB_VEN, USB_HID)
This commit is contained in:
Matthew Parlane 2017-02-07 09:17:05 +13:00 committed by GitHub
commit d244597b42
60 changed files with 3328 additions and 861 deletions

View File

@ -52,7 +52,7 @@
<ClCompile Include="libusb\sync.c" />
<ClCompile Include="libusb\os\threads_windows.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>
<ClInclude Include="msvc\config.h" />
@ -65,7 +65,7 @@
<ClInclude Include="libusb\version_nano.h" />
<ClInclude Include="libusb\os\windows_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>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -34,6 +34,11 @@ set(SRCS Analytics.cpp
Crypto/ec.cpp
Logging/LogManager.cpp)
if(LIBUSB_FOUND)
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
set(SRCS ${SRCS} LibusbContext.cpp)
endif(LIBUSB_FOUND)
if(ANDROID)
set(SRCS ${SRCS}
Logging/ConsoleListenerDroid.cpp)

View File

@ -113,6 +113,7 @@
<ClInclude Include="Hash.h" />
<ClInclude Include="IniFile.h" />
<ClInclude Include="JitRegister.h" />
<ClInclude Include="LibusbContext.h" />
<ClInclude Include="LinearDiskCache.h" />
<ClInclude Include="MathUtil.h" />
<ClInclude Include="MD5.h" />
@ -159,6 +160,9 @@
<ClCompile Include="Hash.cpp" />
<ClCompile Include="IniFile.cpp" />
<ClCompile Include="JitRegister.cpp" />
<ClCompile Include="LibusbContext.cpp">
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="Logging\ConsoleListenerWin.cpp" />
<ClCompile Include="MathUtil.cpp" />
<ClCompile Include="MD5.cpp" />

View File

@ -45,6 +45,7 @@
<ClInclude Include="FPURoundMode.h" />
<ClInclude Include="Hash.h" />
<ClInclude Include="IniFile.h" />
<ClInclude Include="LibusbContext.h" />
<ClInclude Include="LinearDiskCache.h" />
<ClInclude Include="MathUtil.h" />
<ClInclude Include="MemArena.h" />
@ -236,6 +237,7 @@
<ClCompile Include="FileUtil.cpp" />
<ClCompile Include="Hash.cpp" />
<ClCompile Include="IniFile.cpp" />
<ClCompile Include="LibusbContext.cpp" />
<ClCompile Include="MathUtil.cpp" />
<ClCompile Include="MemArena.cpp" />
<ClCompile Include="MemoryUtil.cpp" />

View 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;
}
}

View 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();
}

View File

@ -31,7 +31,6 @@ enum LOG_TYPE
IOS_DI,
IOS_ES,
IOS_FILEIO,
IOS_HID,
IOS_NET,
IOS_SD,
IOS_SSL,

View File

@ -65,7 +65,6 @@ LogManager::LogManager()
m_Log[LogTypes::IOS_DI] = new LogContainer("IOS_DI", "IOS - Drive Interface");
m_Log[LogTypes::IOS_ES] = new LogContainer("IOS_ES", "IOS - ETicket Services");
m_Log[LogTypes::IOS_FILEIO] = new LogContainer("IOS_FILEIO", "IOS - FileIO");
m_Log[LogTypes::IOS_HID] = new LogContainer("IOS_HID", "IOS - USB_HID");
m_Log[LogTypes::IOS_SD] = new LogContainer("IOS_SD", "IOS - SDIO");
m_Log[LogTypes::IOS_SSL] = new LogContainer("IOS_SSL", "IOS - SSL");
m_Log[LogTypes::IOS_STM] = new LogContainer("IOS_STM", "IOS - State Transition Manager");

View File

@ -154,9 +154,15 @@ set(SRCS ActionReplay.cpp
IOS/SDIO/SDIOSlot0.cpp
IOS/STM/STM.cpp
IOS/USB/Common.cpp
IOS/USB/Host.cpp
IOS/USB/OH0/OH0.cpp
IOS/USB/OH0/OH0Device.cpp
IOS/USB/USB_HID/HIDv4.cpp
IOS/USB/USB_VEN/VEN.cpp
IOS/USB/USBV0.cpp
IOS/USB/USBV4.cpp
IOS/USB/USBV5.cpp
IOS/USB/USB_KBD.cpp
IOS/USB/USB_VEN.cpp
IOS/USB/Bluetooth/BTBase.cpp
IOS/USB/Bluetooth/BTEmu.cpp
IOS/USB/Bluetooth/BTStub.cpp
@ -264,7 +270,7 @@ set(LIBS
if(LIBUSB_FOUND)
# Using shared LibUSB
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
set(SRCS ${SRCS} IOS/USB/USB_HIDv4.cpp
set(SRCS ${SRCS} IOS/USB/LibusbDevice.cpp
IOS/USB/Bluetooth/BTReal.cpp)
endif()

View File

@ -77,6 +77,7 @@ void SConfig::SaveSettings()
SaveAnalyticsSettings(ini);
SaveNetworkSettings(ini);
SaveBluetoothPassthroughSettings(ini);
SaveUSBPassthroughSettings(ini);
SaveSysconfSettings(ini);
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
@ -343,6 +344,20 @@ void SConfig::SaveBluetoothPassthroughSettings(IniFile& ini)
section->Set("LinkKeys", m_bt_passthrough_link_keys);
}
void SConfig::SaveUSBPassthroughSettings(IniFile& ini)
{
IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
std::ostringstream oss;
for (const auto& device : m_usb_passthrough_devices)
oss << StringFromFormat("%04x:%04x", device.first, device.second) << ',';
std::string devices_string = oss.str();
if (!devices_string.empty())
devices_string.pop_back();
section->Set("Devices", devices_string);
}
void SConfig::SaveSysconfSettings(IniFile& ini)
{
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
@ -400,6 +415,7 @@ void SConfig::LoadSettings()
LoadNetworkSettings(ini);
LoadAnalyticsSettings(ini);
LoadBluetoothPassthroughSettings(ini);
LoadUSBPassthroughSettings(ini);
LoadSysconfSettings(ini);
}
@ -666,6 +682,27 @@ void SConfig::LoadBluetoothPassthroughSettings(IniFile& ini)
section->Get("LinkKeys", &m_bt_passthrough_link_keys, "");
}
void SConfig::LoadUSBPassthroughSettings(IniFile& ini)
{
IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
m_usb_passthrough_devices.clear();
std::string devices_string;
std::vector<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)
{
IniFile::Section* section = ini.GetOrCreateSection("Sysconf");
@ -757,6 +794,11 @@ void SConfig::LoadDefaults()
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)
{
switch (region)

View File

@ -5,7 +5,9 @@
#pragma once
#include <limits>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "Common/IniFile.h"
@ -151,6 +153,10 @@ struct SConfig : NonCopyable
int m_bt_passthrough_vid = -1;
std::string m_bt_passthrough_link_keys;
// USB passthrough settings
std::set<std::pair<u16, u16>> m_usb_passthrough_devices;
bool IsUSBDeviceWhitelisted(std::pair<u16, u16> vid_pid) const;
// SYSCONF settings
int m_sensor_bar_position = 0x01;
int m_sensor_bar_sensitivity = 0x03;
@ -351,6 +357,7 @@ private:
void SaveNetworkSettings(IniFile& ini);
void SaveAnalyticsSettings(IniFile& ini);
void SaveBluetoothPassthroughSettings(IniFile& ini);
void SaveUSBPassthroughSettings(IniFile& ini);
void SaveSysconfSettings(IniFile& ini);
void LoadGeneralSettings(IniFile& ini);
@ -365,6 +372,7 @@ private:
void LoadNetworkSettings(IniFile& ini);
void LoadAnalyticsSettings(IniFile& ini);
void LoadBluetoothPassthroughSettings(IniFile& ini);
void LoadUSBPassthroughSettings(IniFile& ini);
void LoadSysconfSettings(IniFile& ini);
bool SetRegion(DiscIO::Region region, std::string* directory_name);

View File

@ -54,7 +54,7 @@
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h"
#include "Core/HW/Wiimote.h"
#include "Core/IOS/Network/Socket.h"
#include "Core/IOS/IPC.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
#include "Core/Movie.h"
@ -975,7 +975,7 @@ void UpdateWantDeterminism(bool initial)
bool was_unpaused = Core::PauseAndLock(true);
g_want_determinism = new_want_determinism;
IOS::HLE::WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism);
IOS::HLE::UpdateWantDeterminism(new_want_determinism);
Fifo::UpdateWantDeterminism(new_want_determinism);
// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use
// of FMA.

View File

@ -186,16 +186,20 @@
<ClCompile Include="IOS\SDIO\SDIOSlot0.cpp" />
<ClCompile Include="IOS\STM\STM.cpp" />
<ClCompile Include="IOS\USB\Common.cpp" />
<ClCompile Include="IOS\USB\USBV0.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.
-->
<ClCompile Include="IOS\USB\LibusbDevice.cpp">
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</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_VEN.cpp" />
<ClCompile Include="IOS\USB\Bluetooth\BTBase.cpp" />
<ClCompile Include="IOS\USB\Bluetooth\BTEmu.cpp" />
<ClCompile Include="IOS\USB\Bluetooth\BTStub.cpp" />
@ -419,10 +423,16 @@
<ClInclude Include="IOS\SDIO\SDIOSlot0.h" />
<ClInclude Include="IOS\STM\STM.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\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_VEN.h" />
<ClInclude Include="IOS\USB\Bluetooth\BTBase.h" />
<ClInclude Include="IOS\USB\Bluetooth\BTEmu.h" />
<ClInclude Include="IOS\USB\Bluetooth\BTStub.h" />

View File

@ -773,18 +773,36 @@
<ClCompile Include="IOS\USB\Common.cpp">
<Filter>IOS\USB</Filter>
</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">
<Filter>IOS\USB</Filter>
</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>
</ClCompile>
<ClCompile Include="IOS\USB\USB_KBD.cpp">
<Filter>IOS\USB</Filter>
</ClCompile>
<ClCompile Include="IOS\USB\USB_VEN.cpp">
<Filter>IOS\USB</Filter>
</ClCompile>
<ClCompile Include="IOS\WFS\WFSI.cpp">
<Filter>IOS\WFS</Filter>
</ClCompile>
@ -1353,18 +1371,36 @@
<ClInclude Include="IOS\USB\Common.h">
<Filter>IOS\USB</Filter>
</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">
<Filter>IOS\USB</Filter>
</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>
</ClInclude>
<ClInclude Include="IOS\USB\USB_KBD.h">
<Filter>IOS\USB</Filter>
</ClInclude>
<ClInclude Include="IOS\USB\USB_VEN.h">
<Filter>IOS\USB</Filter>
</ClInclude>
<ClInclude Include="IOS\WFS\WFSI.h">
<Filter>IOS\WFS</Filter>
</ClInclude>

View File

@ -76,6 +76,16 @@ bool IOCtlVRequest::HasInputVectorWithAddress(const u32 vector_address) const
[&](const auto& in_vector) { return in_vector.address == vector_address; });
}
bool IOCtlVRequest::HasNumberOfValidVectors(const size_t in_count, const size_t io_count) const
{
if (in_vectors.size() != in_count || io_vectors.size() != io_count)
return false;
auto IsValidVector = [](const auto& vector) { return vector.size == 0 || vector.address != 0; };
return std::all_of(in_vectors.begin(), in_vectors.end(), IsValidVector) &&
std::all_of(io_vectors.begin(), io_vectors.end(), IsValidVector);
}
void IOCtlRequest::Log(const std::string& device_name, LogTypes::LOG_TYPE type,
LogTypes::LOG_LEVELS verbosity) const
{

View File

@ -42,6 +42,7 @@ enum ReturnCode : s32
FS_EDIRDEPTH = -116, // Max directory depth exceeded
FS_EBUSY = -118, // Resource busy
IPC_EESEXHAUSTED = -1016, // Max of 2 ES handles exceeded
USB_ECANCELED = -7022, // USB OH0 insertion hook cancelled
};
struct Request
@ -122,6 +123,7 @@ struct IOCtlVRequest final : Request
std::vector<IOVector> io_vectors;
explicit IOCtlVRequest(u32 address);
bool HasInputVectorWithAddress(u32 vector_address) const;
bool HasNumberOfValidVectors(size_t in_count, size_t io_count) const;
void Dump(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
LogTypes::LOG_LEVELS level = LogTypes::LINFO) const;
void DumpUnknown(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
@ -137,6 +139,7 @@ public:
{
Static, // Devices which appear in s_device_map.
FileIO, // FileIO devices which are created dynamically.
OH0, // OH0 child devices which are created dynamically.
};
Device(u32 device_id, const std::string& device_name, DeviceType type = DeviceType::Static);
@ -159,6 +162,7 @@ public:
virtual IPCCommandResult IOCtl(const IOCtlRequest& ioctl) { return Unsupported(ioctl); }
virtual IPCCommandResult IOCtlV(const IOCtlVRequest& ioctlv) { return Unsupported(ioctlv); }
virtual void Update() {}
virtual void UpdateWantDeterminism(bool new_want_determinism) {}
virtual DeviceType GetDeviceType() const { return m_device_type; }
virtual bool IsOpened() const { return m_is_active; }
static IPCCommandResult GetDefaultReply(s32 return_value);

View File

@ -44,12 +44,16 @@
#include "Core/IOS/IPC.h"
#include "Core/IOS/Network/Net.h"
#include "Core/IOS/Network/SSL.h"
#include "Core/IOS/Network/Socket.h"
#include "Core/IOS/SDIO/SDIOSlot0.h"
#include "Core/IOS/STM/STM.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/BTReal.h"
#include "Core/IOS/USB/OH0/OH0.h"
#include "Core/IOS/USB/OH0/OH0Device.h"
#include "Core/IOS/USB/USB_HID/HIDv4.h"
#include "Core/IOS/USB/USB_KBD.h"
#include "Core/IOS/USB/USB_VEN.h"
#include "Core/IOS/USB/USB_VEN/VEN.h"
#include "Core/IOS/WFS/WFSI.h"
#include "Core/IOS/WFS/WFSSRV.h"
@ -58,10 +62,6 @@ namespace CoreTiming
struct EventType;
} // namespace CoreTiming
#if defined(__LIBUSB__)
#include "Core/IOS/USB/USB_HIDv4.h"
#endif
namespace IOS
{
namespace HLE
@ -85,6 +85,8 @@ static CoreTiming::EventType* s_event_sdio_notify;
static u64 s_last_reply_time;
static u64 s_active_title_id;
static constexpr u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
static constexpr u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
@ -411,6 +413,14 @@ static void SDIO_EventNotify_CPUThread(u64 userdata, s64 cycles_late)
device->EventNotify();
}
// The title ID is a u64 where the first 32 bits are used for the title type.
// For IOS title IDs, the type will always be 00000001 (system), and the lower 32 bits
// are used for the IOS major version -- which is what we want here.
u32 GetVersion()
{
return static_cast<u32>(s_active_title_id);
}
bool SetupMemory(u64 ios_title_id)
{
auto target_imv = std::find_if(
@ -423,6 +433,8 @@ bool SetupMemory(u64 ios_title_id)
return false;
}
s_active_title_id = ios_title_id;
Memory::Write_U32(target_imv->mem1_physical_size, ADDR_MEM1_SIZE);
Memory::Write_U32(target_imv->mem1_simulated_size, ADDR_MEM1_SIM_SIZE);
Memory::Write_U32(target_imv->mem1_end, ADDR_MEM1_END);
@ -495,15 +507,12 @@ void Reinit()
AddDevice<Device::NetIPTop>("/dev/net/ip/top");
AddDevice<Device::NetSSL>("/dev/net/ssl");
AddDevice<Device::USB_KBD>("/dev/usb/kbd");
AddDevice<Device::USB_VEN>("/dev/usb/ven");
AddDevice<Device::SDIOSlot0>("/dev/sdio/slot0");
AddDevice<Device::Stub>("/dev/sdio/slot1");
#if defined(__LIBUSB__)
AddDevice<Device::USB_HIDv4>("/dev/usb/hid");
#else
AddDevice<Device::Stub>("/dev/usb/hid");
#endif
AddDevice<Device::OH0>("/dev/usb/oh0");
AddDevice<Device::Stub>("/dev/usb/oh1");
AddDevice<Device::USB_VEN>("/dev/usb/ven");
AddDevice<Device::WFSSRV>("/dev/usb/wfssrv");
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]->DoState(p);
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)
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)
{
device = GetDeviceByName(request.path);
@ -851,5 +868,12 @@ void UpdateDevices()
}
}
}
void UpdateWantDeterminism(const bool new_want_determinism)
{
WiiSockMan::GetInstance().UpdateWantDeterminism(new_want_determinism);
for (const auto& device : s_device_map)
device.second->UpdateWantDeterminism(new_want_determinism);
}
} // namespace HLE
} // namespace IOS

View File

@ -51,6 +51,8 @@ void Init();
// Needs to be called after Reset(true) to recreate the device tree
void Reinit();
u32 GetVersion();
bool SetupMemory(u64 ios_title_id);
// Shutdown
@ -77,6 +79,8 @@ void Update();
// Update Devices
void UpdateDevices();
void UpdateWantDeterminism(bool new_want_determinism);
void ExecuteCommand(u32 address);
void EnqueueRequest(u32 address);

View File

@ -16,6 +16,7 @@
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonFuncs.h"
#include "Common/LibusbContext.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/Network.h"
@ -59,8 +60,8 @@ namespace Device
BluetoothReal::BluetoothReal(u32 device_id, const std::string& device_name)
: BluetoothBase(device_id, device_name)
{
const int ret = libusb_init(&m_libusb_context);
_assert_msg_(IOS_WIIMOTE, ret == 0, "Failed to init libusb.");
m_libusb_context = LibusbContext::Get();
_assert_msg_(IOS_WIIMOTE, m_libusb_context, "Failed to init libusb.");
LoadLinkKeys();
}
@ -78,15 +79,13 @@ BluetoothReal::~BluetoothReal()
libusb_unref_device(m_device);
}
libusb_exit(m_libusb_context);
SaveLinkKeys();
}
ReturnCode BluetoothReal::Open(const OpenRequest& request)
{
libusb_device** list;
const ssize_t cnt = libusb_get_device_list(m_libusb_context, &list);
const ssize_t cnt = libusb_get_device_list(m_libusb_context.get(), &list);
_dbg_assert_msg_(IOS, cnt > 0, "Couldn't get device list");
for (ssize_t i = 0; i < cnt; ++i)
{
@ -601,7 +600,7 @@ void BluetoothReal::TransferThread()
Common::SetCurrentThreadName("BT USB Thread");
while (m_thread_running.IsSet())
{
libusb_handle_events_completed(m_libusb_context, nullptr);
libusb_handle_events_completed(m_libusb_context.get(), nullptr);
}
}

View File

@ -76,7 +76,7 @@ private:
libusb_device* m_device = nullptr;
libusb_device_handle* m_handle = nullptr;
libusb_context* m_libusb_context = nullptr;
std::shared_ptr<libusb_context> m_libusb_context;
Common::Flag m_thread_running;
std::thread m_thread;

View File

@ -2,10 +2,15 @@
// Licensed under GPLv2+
// 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/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/USB/Common.h"
namespace IOS
{
@ -26,6 +31,121 @@ void TransferCommand::FillBuffer(const u8* src, const size_t size) const
_assert_msg_(IOS_USB, size == 0 || data_address != 0, "Invalid data_address");
Memory::CopyToEmu(data_address, src, size);
}
void IsoMessage::SetPacketReturnValue(const size_t packet_num, const u16 return_value) const
{
Memory::Write_U16(return_value, static_cast<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 HLE
} // namespace IOS

View File

@ -5,7 +5,9 @@
#pragma once
#include <cstddef>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
@ -17,6 +19,29 @@ namespace HLE
{
namespace USB
{
enum StandardDeviceRequestCodes
{
REQUEST_GET_DESCRIPTOR = 6,
REQUEST_SET_CONFIGURATION = 9,
REQUEST_GET_INTERFACE = 10,
REQUEST_SET_INTERFACE = 11,
};
enum ControlRequestTypes
{
DIR_HOST2DEVICE = 0,
DIR_DEVICE2HOST = 1,
TYPE_STANDARD = 0,
TYPE_VENDOR = 2,
REC_DEVICE = 0,
REC_INTERFACE = 1,
};
constexpr u16 USBHDR(u8 dir, u8 type, u8 recipient, u8 request)
{
return static_cast<u16>(((dir << 7 | type << 5 | recipient) << 8) | request);
}
struct DeviceDescriptor
{
u8 bLength;
@ -109,6 +134,49 @@ struct IntrMessage : TransferCommand
u8 endpoint = 0;
using TransferCommand::TransferCommand;
};
struct IsoMessage : TransferCommand
{
u32 packet_sizes_addr = 0;
std::vector<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 HLE
} // namespace IOS

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -42,6 +42,17 @@ V0IntrMessage::V0IntrMessage(const IOCtlVRequest& ioctlv)
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].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 HLE
} // namespace IOS

View File

@ -4,8 +4,6 @@
#pragma once
#include <cstddef>
#include "Common/CommonTypes.h"
#include "Core/IOS/USB/Common.h"
@ -24,6 +22,21 @@ enum V0Requests
IOCTLV_USBV0_CTRLMSG = 0,
IOCTLV_USBV0_BLKMSG = 1,
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
@ -40,6 +53,11 @@ struct V0IntrMessage final : IntrMessage
{
explicit V0IntrMessage(const IOCtlVRequest& ioctlv);
};
struct V0IsoMessage final : IsoMessage
{
explicit V0IsoMessage(const IOCtlVRequest& ioctlv);
};
} // namespace USB
} // namespace HLE
} // namespace IOS

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View 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

View File

@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// 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.
// Versions after 42 don't need to be added to this list,

View File

@ -7,6 +7,7 @@ set(GUI_SRCS
Cheats/CheatsWindow.cpp
Cheats/CreateCodeDialog.cpp
Cheats/GeckoCodeDiag.cpp
Config/AddUSBDeviceDiag.cpp
Config/AdvancedConfigPane.cpp
Config/AudioConfigPane.cpp
Config/ConfigMain.cpp

View 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();
}

View 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;
};

View File

@ -2,11 +2,11 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinWX/Config/WiiConfigPane.h"
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/choice.h>
#include <wx/gbsizer.h>
#include <wx/listbox.h>
#include <wx/sizer.h>
#include <wx/slider.h>
#include <wx/stattext.h>
@ -14,9 +14,12 @@
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/IOS/IPC.h"
#include "DolphinWX/Config/AddUSBDeviceDiag.h"
#include "DolphinWX/Config/WiiConfigPane.h"
#include "DolphinWX/DolphinSlider.h"
#include "DolphinWX/WxEventUtils.h"
#include "DolphinWX/WxUtils.h"
#include "UICommon/USBUtils.h"
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);
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_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 =
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);
@ -79,6 +86,12 @@ void WiiConfigPane::InitializeGUI()
misc_settings_grid_sizer->Add(m_system_language_choice, wxGBPosition(3, 1), wxDefaultSpan,
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);
bt_sensor_bar_pos_sizer->Add(new wxStaticText(this, wxID_ANY, _("Min")), 0,
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->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 =
new wxStaticBoxSizer(wxVERTICAL, this, _("Wii Remote Settings"));
bt_settings_static_sizer->AddSpacer(space5);
@ -135,6 +157,8 @@ void WiiConfigPane::InitializeGUI()
main_sizer->AddSpacer(space5);
main_sizer->Add(device_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 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->AddSpacer(space5);
@ -151,12 +175,26 @@ void WiiConfigPane::LoadGUIValues()
m_sd_card_checkbox->SetValue(SConfig::GetInstance().m_WiiSDCard);
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_sens->SetValue(SConfig::GetInstance().m_sensor_bar_sensitivity);
m_bt_speaker_volume->SetValue(SConfig::GetInstance().m_speaker_volume);
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()
{
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_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)

View File

@ -6,11 +6,12 @@
#include <wx/arrstr.h>
#include <wx/panel.h>
#include "Common/CommonTypes.h"
class DolphinSlider;
class wxButton;
class wxCheckBox;
class wxChoice;
class wxListBox;
class wxSlider;
class WiiConfigPane final : public wxPanel
@ -23,6 +24,8 @@ private:
void LoadGUIValues();
void BindEvents();
void PopulateUSBPassthroughListbox();
void OnScreenSaverCheckBoxChanged(wxCommandEvent&);
void OnPAL60CheckBoxChanged(wxCommandEvent&);
void OnSDCardCheckBoxChanged(wxCommandEvent&);
@ -30,6 +33,10 @@ private:
void OnSystemLanguageChoiceChanged(wxCommandEvent&);
void OnAspectRatioChoiceChanged(wxCommandEvent&);
void OnUSBWhitelistAddButton(wxCommandEvent&);
void OnUSBWhitelistRemoveButton(wxCommandEvent&);
void OnUSBWhitelistRemoveButtonUpdate(wxUpdateUIEvent&);
void OnSensorBarPosChanged(wxCommandEvent&);
void OnSensorBarSensChanged(wxCommandEvent&);
void OnSpeakerVolumeChanged(wxCommandEvent&);
@ -46,6 +53,10 @@ private:
wxChoice* m_system_language_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;
DolphinSlider* m_bt_sensor_bar_sens;
DolphinSlider* m_bt_speaker_volume;

View File

@ -62,6 +62,7 @@
<ClCompile Include="Cheats\CheatsWindow.cpp" />
<ClCompile Include="Cheats\CreateCodeDialog.cpp" />
<ClCompile Include="Cheats\GeckoCodeDiag.cpp" />
<ClCompile Include="Config\AddUSBDeviceDiag.cpp" />
<ClCompile Include="Config\AdvancedConfigPane.cpp" />
<ClCompile Include="Config\AudioConfigPane.cpp" />
<ClCompile Include="Config\ConfigMain.cpp" />
@ -139,6 +140,7 @@
<ItemGroup>
<ClInclude Include="Cheats\ActionReplayCodesPanel.h" />
<ClInclude Include="Cheats\ARCodeAddEdit.h" />
<ClInclude Include="Config\AddUSBDeviceDiag.h" />
<ClInclude Include="Config\AdvancedConfigPane.h" />
<ClInclude Include="Config\AudioConfigPane.h" />
<ClInclude Include="Config\GameCubeConfigPane.h" />

View File

@ -224,6 +224,9 @@
<ClCompile Include="Config\PathConfigPane.cpp">
<Filter>GUI\Config</Filter>
</ClCompile>
<ClCompile Include="Config\AddUSBDeviceDiag.cpp">
<Filter>GUI\Config</Filter>
</ClCompile>
<ClCompile Include="Config\AdvancedConfigPane.cpp">
<Filter>GUI\Config</Filter>
</ClCompile>
@ -443,6 +446,9 @@
<ClInclude Include="Config\PathConfigPane.h">
<Filter>GUI\Config</Filter>
</ClInclude>
<ClCompile Include="Config\AddUSBDeviceDiag.h">
<Filter>GUI\Config</Filter>
</ClCompile>
<ClInclude Include="Config\AdvancedConfigPane.h">
<Filter>GUI\Config</Filter>
</ClInclude>

View File

@ -4,9 +4,11 @@
#include <algorithm>
#include <libusb.h>
#include <memory>
#include <mutex>
#include "Common/Flag.h"
#include "Common/LibusbContext.h"
#include "Common/Logging/Log.h"
#include "Common/Thread.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 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
static bool s_libusb_hotplug_enabled = true;
#else
@ -116,7 +118,7 @@ static void ScanThreadFunc()
if (s_libusb_hotplug_enabled)
{
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_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback,
nullptr, &s_hotplug_handle) != LIBUSB_SUCCESS)
@ -131,7 +133,7 @@ static void ScanThreadFunc()
if (s_libusb_hotplug_enabled)
{
static timeval tv = {0, 500000};
libusb_handle_events_timeout(s_libusb_context, &tv);
libusb_handle_events_timeout(s_libusb_context.get(), &tv);
}
else
{
@ -168,11 +170,9 @@ void Init()
s_libusb_driver_not_supported = false;
int ret = libusb_init(&s_libusb_context);
if (ret)
s_libusb_context = LibusbContext::Get();
if (!s_libusb_context)
{
ERROR_LOG(SERIALINTERFACE, "libusb_init failed with error: %d", ret);
s_libusb_driver_not_supported = true;
Shutdown();
}
@ -203,7 +203,7 @@ void StopScanThread()
static void Setup()
{
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++)
{
@ -335,16 +335,11 @@ void Shutdown()
StopScanThread();
#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102
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
Reset();
if (s_libusb_context)
{
libusb_exit(s_libusb_context);
s_libusb_context = nullptr;
}
s_libusb_context.reset();
s_libusb_driver_not_supported = false;
}

View File

@ -1,6 +1,10 @@
set(SRCS Disassembler.cpp
UICommon.cpp)
UICommon.cpp
USBUtils.cpp)
set(LIBS common)
if(LIBUSB_FOUND)
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
endif()
add_dolphin_library(uicommon "${SRCS}" "${LIBS}")

View File

@ -17,6 +17,7 @@
#include "InputCommon/GCAdapter.h"
#include "UICommon/UICommon.h"
#include "UICommon/USBUtils.h"
#include "VideoCommon/VideoBackendBase.h"
@ -29,6 +30,7 @@ void Init()
VideoBackendBase::PopulateList();
WiimoteReal::LoadSettings();
GCAdapter::Init();
USBUtils::Init();
VideoBackendBase::ActivateBackend(SConfig::GetInstance().m_strVideoBackend);
SetEnableAlert(SConfig::GetInstance().bUsePanicHandlers);
@ -40,6 +42,7 @@ void Shutdown()
WiimoteReal::Shutdown();
VideoBackendBase::ClearList();
SConfig::Shutdown();
USBUtils::Shutdown();
LogManager::Shutdown();
}

View File

@ -45,10 +45,14 @@
<ItemGroup>
<ClCompile Include="UICommon.cpp" />
<ClCompile Include="Disassembler.cpp" />
<ClCompile Include="USBUtils.cpp">
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="UICommon.h" />
<ClInclude Include="Disassembler.h" />
<ClInclude Include="USBUtils.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View 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

View 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

View File

@ -51,7 +51,7 @@
<AdditionalIncludeDirectories>$(ExternalsDir)xxhash;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<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>SFML_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>CURL_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>