From ac3b866083b763137aaff89cdcda2c5d8deb6d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 3 Nov 2017 11:17:12 +0100 Subject: [PATCH] USB_HIDv5: Submit interrupt transfers to the correct endpoint Unlike VEN, the endpoint is determined by the value at 8-12. If it's non-zero, HID submits the request to the interrupt OUT endpoint. Otherwise, the request is submitted to the IN endpoint. This commit changes HIDv5 to keep track of endpoints (like IOS does) and use them when submitting interrupt transfers. --- Source/Core/Core/IOS/USB/USBV5.cpp | 17 ---------- Source/Core/Core/IOS/USB/USBV5.h | 1 - Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp | 39 ++++++++++++++++++++-- Source/Core/Core/IOS/USB/USB_HID/HIDv5.h | 7 ++++ Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp | 17 ++++++++++ Source/Core/Core/IOS/USB/USB_VEN/VEN.h | 1 + 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Source/Core/Core/IOS/USB/USBV5.cpp b/Source/Core/Core/IOS/USB/USBV5.cpp index d4712c9488..804989b75a 100644 --- a/Source/Core/Core/IOS/USB/USBV5.cpp +++ b/Source/Core/Core/IOS/USB/USBV5.cpp @@ -174,23 +174,6 @@ IPCCommandResult USBV5ResourceManager::SuspendResume(USBV5Device& device, return GetDefaultReply(IPC_SUCCESS); } -s32 USBV5ResourceManager::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv) -{ - switch (ioctlv.request) - { - case USB::IOCTLV_USBV5_CTRLMSG: - return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); - case USB::IOCTLV_USBV5_INTRMSG: - return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); - case USB::IOCTLV_USBV5_BULKMSG: - return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); - case USB::IOCTLV_USBV5_ISOMSG: - return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); - default: - return IPC_EINVAL; - } -} - IPCCommandResult USBV5ResourceManager::HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler) { diff --git a/Source/Core/Core/IOS/USB/USBV5.h b/Source/Core/Core/IOS/USB/USBV5.h index fbfde46fb3..ceca6bcb71 100644 --- a/Source/Core/Core/IOS/USB/USBV5.h +++ b/Source/Core/Core/IOS/USB/USBV5.h @@ -86,7 +86,6 @@ protected: IPCCommandResult SetAlternateSetting(USBV5Device& device, const IOCtlRequest& request); IPCCommandResult Shutdown(const IOCtlRequest& request); IPCCommandResult SuspendResume(USBV5Device& device, const IOCtlRequest& request); - s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request); using Handler = std::function; IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler); diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp index dfc12f6b6a..c8f6b8f241 100644 --- a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp @@ -71,13 +71,40 @@ IPCCommandResult USB_HIDv5::IOCtlV(const IOCtlVRequest& request) auto host_device = GetDeviceById(device->host_id); host_device->Attach(device->interface_number); return HandleTransfer(host_device, request.request, - [&, this]() { return SubmitTransfer(*host_device, request); }); + [&, this]() { return SubmitTransfer(*device, *host_device, request); }); } default: return GetDefaultReply(IPC_EINVAL); } } +s32 USB_HIDv5::SubmitTransfer(USBV5Device& device, USB::Device& host_device, + const IOCtlVRequest& ioctlv) +{ + switch (ioctlv.request) + { + case USB::IOCTLV_USBV5_CTRLMSG: + return host_device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); + case USB::IOCTLV_USBV5_INTRMSG: + { + auto message = std::make_unique(m_ios, ioctlv); + + // Unlike VEN, the endpoint is determined by the value at 8-12. + // If it's non-zero, HID submits the request to the interrupt OUT endpoint. + // Otherwise, the request is submitted to the IN endpoint. + AdditionalDeviceData* data = &m_additional_device_data[&device - m_usbv5_devices.data()]; + if (Memory::Read_U32(ioctlv.in_vectors[0].address + 8) != 0) + message->endpoint = data->interrupt_out_endpoint; + else + message->endpoint = data->interrupt_in_endpoint; + + return host_device.SubmitTransfer(std::move(message)); + } + default: + return IPC_EINVAL; + } +} + IPCCommandResult USB_HIDv5::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request) { // FIXME: Unlike VEN, there are 3 valid values for the endpoint, @@ -128,7 +155,15 @@ IPCCommandResult USB_HIDv5::GetDeviceInfo(USBV5Device& device, const IOCtlReques constexpr u8 ENDPOINT_IN = 0x80; if (endpoint.bmAttributes == ENDPOINT_INTERRUPT) { - const u32 offset = (endpoint.bEndpointAddress & ENDPOINT_IN) != 0 ? 80 : 88; + const bool is_in_endpoint = (endpoint.bEndpointAddress & ENDPOINT_IN) != 0; + + AdditionalDeviceData* data = &m_additional_device_data[&device - m_usbv5_devices.data()]; + if (is_in_endpoint) + data->interrupt_in_endpoint = endpoint.bEndpointAddress; + else + data->interrupt_out_endpoint = endpoint.bEndpointAddress; + + const u32 offset = is_in_endpoint ? 80 : 88; endpoint.Swap(); Memory::CopyToEmu(request.buffer_out + offset, &endpoint, sizeof(endpoint)); } diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h index 625a810f54..82709898fa 100644 --- a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h @@ -27,9 +27,16 @@ public: private: IPCCommandResult CancelEndpoint(USBV5Device& device, const IOCtlRequest& request); IPCCommandResult GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request); + s32 SubmitTransfer(USBV5Device& device, USB::Device& host_device, const IOCtlVRequest& ioctlv); bool ShouldAddDevice(const USB::Device& device) const override; bool HasInterfaceNumberInIDs() const override { return true; } + struct AdditionalDeviceData + { + u8 interrupt_in_endpoint = 0; + u8 interrupt_out_endpoint = 0; + }; + std::array m_additional_device_data{}; }; } // namespace Device } // namespace HLE diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp index fcc7e3792b..93fad5d0c1 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -95,6 +95,23 @@ IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request) } } +s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv) +{ + switch (ioctlv.request) + { + case USB::IOCTLV_USBV5_CTRLMSG: + return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); + case USB::IOCTLV_USBV5_INTRMSG: + return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); + case USB::IOCTLV_USBV5_BULKMSG: + return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); + case USB::IOCTLV_USBV5_ISOMSG: + return device.SubmitTransfer(std::make_unique(m_ios, ioctlv)); + default: + return IPC_EINVAL; + } +} + IPCCommandResult USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request) { const u8 endpoint = static_cast(Memory::Read_U32(request.buffer_in + 8)); diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.h b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h index 58c4af767e..8205b0ca2d 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.h +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h @@ -29,6 +29,7 @@ private: IPCCommandResult CancelEndpoint(USBV5Device& device, const IOCtlRequest& request); IPCCommandResult GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request); + s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv); bool HasInterfaceNumberInIDs() const override { return false; } }; } // namespace Device