From c9f4889437db16fc752cc0c998f1453af4f7d262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 12 Nov 2016 19:05:32 +0100 Subject: [PATCH] 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. --- Source/Core/Common/Logging/Log.h | 1 - Source/Core/Common/Logging/LogManager.cpp | 1 - Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/Core.vcxproj | 12 +- Source/Core/Core/Core.vcxproj.filters | 10 +- Source/Core/Core/IOS/IPC.cpp | 9 +- Source/Core/Core/IOS/USB/USBV4.cpp | 97 ++++ Source/Core/Core/IOS/USB/USBV4.h | 50 ++ Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp | 230 +++++++++ Source/Core/Core/IOS/USB/USB_HID/HIDv4.h | 62 +++ Source/Core/Core/IOS/USB/USB_HIDv4.cpp | 575 --------------------- Source/Core/Core/IOS/USB/USB_HIDv4.h | 89 ---- 12 files changed, 454 insertions(+), 685 deletions(-) create mode 100644 Source/Core/Core/IOS/USB/USBV4.cpp create mode 100644 Source/Core/Core/IOS/USB/USBV4.h create mode 100644 Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp create mode 100644 Source/Core/Core/IOS/USB/USB_HID/HIDv4.h delete mode 100644 Source/Core/Core/IOS/USB/USB_HIDv4.cpp delete mode 100644 Source/Core/Core/IOS/USB/USB_HIDv4.h diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h index ce46bcb8e2..3ef07acbf1 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -31,7 +31,6 @@ enum LOG_TYPE IOS_DI, IOS_ES, IOS_FILEIO, - IOS_HID, IOS_NET, IOS_SD, IOS_SSL, diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index 4cb786095b..964a8df973 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -65,7 +65,6 @@ LogManager::LogManager() m_Log[LogTypes::IOS_DI] = new LogContainer("IOS_DI", "IOS - Drive Interface"); m_Log[LogTypes::IOS_ES] = new LogContainer("IOS_ES", "IOS - ETicket Services"); m_Log[LogTypes::IOS_FILEIO] = new LogContainer("IOS_FILEIO", "IOS - FileIO"); - m_Log[LogTypes::IOS_HID] = new LogContainer("IOS_HID", "IOS - USB_HID"); m_Log[LogTypes::IOS_SD] = new LogContainer("IOS_SD", "IOS - SDIO"); m_Log[LogTypes::IOS_SSL] = new LogContainer("IOS_SSL", "IOS - SSL"); m_Log[LogTypes::IOS_STM] = new LogContainer("IOS_STM", "IOS - State Transition Manager"); diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 44bae6b2c6..594608b377 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -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() diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 912c038179..7983948be3 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -194,14 +194,9 @@ + - - - 4200;%(DisableSpecificWarnings) - + @@ -431,8 +426,9 @@ + - + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 0c7ba536db..3b9ad3503a 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -785,10 +785,13 @@ IOS\USB + + IOS\USB + IOS\USB - + IOS\USB @@ -1377,10 +1380,13 @@ IOS\USB + + IOS\USB + IOS\USB - + IOS\USB diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 16443efa7e..0db81b2efc 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -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("/dev/usb/ven"); AddDevice("/dev/sdio/slot0"); AddDevice("/dev/sdio/slot1"); -#if defined(__LIBUSB__) AddDevice("/dev/usb/hid"); -#else - AddDevice("/dev/usb/hid"); -#endif AddDevice("/dev/usb/oh0"); AddDevice("/dev/usb/oh1"); AddDevice("/dev/usb/wfssrv"); diff --git a/Source/Core/Core/IOS/USB/USBV4.cpp b/Source/Core/Core/IOS/USB/USBV4.cpp new file mode 100644 index 0000000000..fae4e2011a --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV4.cpp @@ -0,0 +1,97 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#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(Common::swap32(hid_request.interrupt.length)); + endpoint = static_cast(Common::swap32(hid_request.interrupt.endpoint)); + data_address = Common::swap32(hid_request.data_addr); +} +} // namespace USB +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USBV4.h b/Source/Core/Core/IOS/USB/USBV4.h new file mode 100644 index 0000000000..93ccd7ee21 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV4.h @@ -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 diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp new file mode 100644 index 0000000000..4734490f1c --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp @@ -0,0 +1,230 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#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 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(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 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(request)); + case USB::IOCTL_USBV4_GET_US_STRING: + return device.SubmitTransfer(std::make_unique(request)); + case USB::IOCTL_USBV4_INTRMSG_IN: + case USB::IOCTL_USBV4_INTRMSG_OUT: + return device.SubmitTransfer(std::make_unique(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(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_HIDv4::GetDeviceByIOSID(const s32 ios_id) const +{ + std::lock_guard 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 device) +{ + { + std::lock_guard 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 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 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 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(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 USB_HIDv4::GetDeviceEntry(const USB::Device& device) const +{ + std::lock_guard 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 entry(8); + const std::vector descriptors = device.GetDescriptorsUSBV4(); + const u32 entry_size = Common::swap32(static_cast(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 diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h new file mode 100644 index 0000000000..4048f3c339 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h @@ -0,0 +1,62 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +#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 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 GetDeviceEntry(const USB::Device& device) const; + void OnDeviceChange(ChangeEvent, std::shared_ptr) 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 m_devicechange_hook_request; + + mutable std::mutex m_id_map_mutex; + // IOS device IDs <=> USB device IDs + std::map m_ios_ids; + std::map m_device_ids; +}; +} // namespace Device +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_HIDv4.cpp b/Source/Core/Core/IOS/USB/USB_HIDv4.cpp deleted file mode 100644 index f52fe5607a..0000000000 --- a/Source/Core/Core/IOS/USB/USB_HIDv4.cpp +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2012 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include - -#include - -#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 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 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 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 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 diff --git a/Source/Core/Core/IOS/USB/USB_HIDv4.h b/Source/Core/Core/IOS/USB/USB_HIDv4.h deleted file mode 100644 index 0732a91b44..0000000000 --- a/Source/Core/Core/IOS/USB/USB_HIDv4.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2012 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include - -#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 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