IOS: Re-implement USB_HIDv4 (/dev/usb/hid)

This reimplements the USB HID v4 IOS device using the new common
USB code (to reuse more code and allow emulated HIDs to be added
more easily in the future).

The main difference is that HIDs now have to be whitelisted, like
every other USB device for OH0 and VEN.
This commit is contained in:
Léo Lam 2016-11-12 19:05:32 +01:00
parent ee188a1d5a
commit c9f4889437
12 changed files with 454 additions and 685 deletions

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

@ -157,7 +157,9 @@ set(SRCS ActionReplay.cpp
IOS/USB/Host.cpp
IOS/USB/OH0/OH0.cpp
IOS/USB/OH0/OH0Device.cpp
IOS/USB/USB_HID/HIDv4.cpp
IOS/USB/USBV0.cpp
IOS/USB/USBV4.cpp
IOS/USB/USB_KBD.cpp
IOS/USB/USB_VEN.cpp
IOS/USB/Bluetooth/BTBase.cpp
@ -268,7 +270,6 @@ if(LIBUSB_FOUND)
# Using shared LibUSB
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
set(SRCS ${SRCS} IOS/USB/LibusbDevice.cpp
IOS/USB/USB_HIDv4.cpp
IOS/USB/Bluetooth/BTReal.cpp)
endif()

View File

@ -194,14 +194,9 @@
</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\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.
-->
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="IOS\USB\USBV4.cpp" />
<ClCompile Include="IOS\USB\USB_KBD.cpp" />
<ClCompile Include="IOS\USB\USB_VEN.cpp" />
<ClCompile Include="IOS\USB\Bluetooth\BTBase.cpp" />
@ -431,8 +426,9 @@
<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\USBV0.h" />
<ClInclude Include="IOS\USB\USB_HIDv4.h" />
<ClInclude Include="IOS\USB\USBV4.h" />
<ClInclude Include="IOS\USB\USB_KBD.h" />
<ClInclude Include="IOS\USB\USB_VEN.h" />
<ClInclude Include="IOS\USB\Bluetooth\BTBase.h" />

View File

@ -785,10 +785,13 @@
<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\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\USB_KBD.cpp">
@ -1377,10 +1380,13 @@
<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\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\USB_KBD.h">

View File

@ -51,6 +51,7 @@
#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/WFS/WFSI.h"
@ -61,10 +62,6 @@ namespace CoreTiming
struct EventType;
} // namespace CoreTiming
#if defined(__LIBUSB__)
#include "Core/IOS/USB/USB_HIDv4.h"
#endif
namespace IOS
{
namespace HLE
@ -513,11 +510,7 @@ void Reinit()
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::WFSSRV>("/dev/usb/wfssrv");

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