From 5226d6103a000fb1a0b6a3fb35c13433d9ac8e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 1 May 2019 15:55:13 +0200 Subject: [PATCH 1/7] IOS/USB: Add debug logging for all transfers This makes debugging USB issues easier. --- Source/Core/Core/IOS/USB/LibusbDevice.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.cpp b/Source/Core/Core/IOS/USB/LibusbDevice.cpp index 0821cfacbe..736695cab7 100644 --- a/Source/Core/Core/IOS/USB/LibusbDevice.cpp +++ b/Source/Core/Core/IOS/USB/LibusbDevice.cpp @@ -18,6 +18,7 @@ #include "Common/Assert.h" #include "Common/Logging/Log.h" +#include "Common/StringUtil.h" #include "Core/HW/Memmap.h" #include "Core/IOS/Device.h" #include "Core/IOS/IOS.h" @@ -184,11 +185,19 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) if (!m_device_attached) return LIBUSB_ERROR_NOT_FOUND; + DEBUG_LOG(IOS_USB, + "[%04x:%04x %d] Control: bRequestType=%02x bRequest=%02x wValue=%04x" + " wIndex=%04x wLength=%04x", + m_vid, m_pid, m_active_interface, cmd->request_type, cmd->request, cmd->value, + cmd->index, cmd->length); + 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): { + INFO_LOG(IOS_USB, "[%04x:%04x %d] REQUEST_SET_INTERFACE index=%04x value=%04x", m_vid, m_pid, + m_active_interface, cmd->index, cmd->value); if (static_cast(cmd->index) != m_active_interface) { const int ret = ChangeInterface(static_cast(cmd->index)); @@ -206,6 +215,8 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) } case USBHDR(DIR_HOST2DEVICE, TYPE_STANDARD, REC_DEVICE, REQUEST_SET_CONFIGURATION): { + INFO_LOG(IOS_USB, "[%04x:%04x %d] REQUEST_SET_CONFIGURATION index=%04x value=%04x", m_vid, + m_pid, m_active_interface, cmd->index, cmd->value); const int ret = libusb_set_configuration(m_handle, cmd->value); if (ret == 0) m_ios.EnqueueIPCReply(cmd->ios_request, cmd->length); @@ -230,6 +241,9 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) if (!m_device_attached) return LIBUSB_ERROR_NOT_FOUND; + DEBUG_LOG(IOS_USB, "[%04x:%04x %d] Bulk: length=%04x endpoint=%02x", m_vid, m_pid, + m_active_interface, cmd->length, cmd->endpoint); + libusb_transfer* transfer = libusb_alloc_transfer(0); libusb_fill_bulk_transfer(transfer, m_handle, cmd->endpoint, cmd->MakeBuffer(cmd->length).release(), cmd->length, TransferCallback, @@ -244,6 +258,9 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) if (!m_device_attached) return LIBUSB_ERROR_NOT_FOUND; + DEBUG_LOG(IOS_USB, "[%04x:%04x %d] Interrupt: length=%04x endpoint=%02x", m_vid, m_pid, + m_active_interface, cmd->length, cmd->endpoint); + libusb_transfer* transfer = libusb_alloc_transfer(0); libusb_fill_interrupt_transfer(transfer, m_handle, cmd->endpoint, cmd->MakeBuffer(cmd->length).release(), cmd->length, @@ -258,6 +275,9 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) if (!m_device_attached) return LIBUSB_ERROR_NOT_FOUND; + DEBUG_LOG(IOS_USB, "[%04x:%04x %d] Isochronous: length=%04x endpoint=%02x num_packets=%02x", + m_vid, m_pid, m_active_interface, cmd->length, cmd->endpoint, cmd->num_packets); + libusb_transfer* transfer = libusb_alloc_transfer(cmd->num_packets); transfer->buffer = cmd->MakeBuffer(cmd->length).release(); transfer->callback = TransferCallback; From a6da38d75dce7941262a64b0097a9e2502bff22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 2 May 2019 18:56:10 +0200 Subject: [PATCH 2/7] IOS/USB: Fix TransferCommand length type The total buffer size for isochronous transfers should be a u32, not a u16. It is easy to hit the bug with devices such as cameras, which require larger buffers. This fixes Your Shape. This also fixes the length type for bulk and interrupt transfers, which should be u32 as that's what IOS supports. I'm not sure why I made them u16... probably because OH0 uses u16 for most lengths... --- Source/Core/Core/IOS/USB/Common.h | 6 +++--- Source/Core/Core/IOS/USB/USBV4.cpp | 2 +- Source/Core/Core/IOS/USB/USBV5.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Core/Core/IOS/USB/Common.h b/Source/Core/Core/IOS/USB/Common.h index 58a164adc1..8a6c4edf06 100644 --- a/Source/Core/Core/IOS/USB/Common.h +++ b/Source/Core/Core/IOS/USB/Common.h @@ -127,14 +127,14 @@ struct CtrlMessage : TransferCommand struct BulkMessage : TransferCommand { - u16 length = 0; + u32 length = 0; u8 endpoint = 0; using TransferCommand::TransferCommand; }; struct IntrMessage : TransferCommand { - u16 length = 0; + u32 length = 0; u8 endpoint = 0; using TransferCommand::TransferCommand; }; @@ -143,7 +143,7 @@ struct IsoMessage : TransferCommand { u32 packet_sizes_addr = 0; std::vector packet_sizes; - u16 length = 0; + u32 length = 0; u8 num_packets = 0; u8 endpoint = 0; using TransferCommand::TransferCommand; diff --git a/Source/Core/Core/IOS/USB/USBV4.cpp b/Source/Core/Core/IOS/USB/USBV4.cpp index c31848fe1c..1aae6c6a7b 100644 --- a/Source/Core/Core/IOS/USB/USBV4.cpp +++ b/Source/Core/Core/IOS/USB/USBV4.cpp @@ -87,7 +87,7 @@ V4IntrMessage::V4IntrMessage(Kernel& ios, const IOCtlRequest& ioctl) : IntrMessa { HIDRequest hid_request; Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request)); - length = static_cast(Common::swap32(hid_request.interrupt.length)); + length = Common::swap32(hid_request.interrupt.length); endpoint = static_cast(Common::swap32(hid_request.interrupt.endpoint)); data_address = Common::swap32(hid_request.data_addr); } diff --git a/Source/Core/Core/IOS/USB/USBV5.cpp b/Source/Core/Core/IOS/USB/USBV5.cpp index 09eb3d6718..0da9448a65 100644 --- a/Source/Core/Core/IOS/USB/USBV5.cpp +++ b/Source/Core/Core/IOS/USB/USBV5.cpp @@ -31,14 +31,14 @@ V5CtrlMessage::V5CtrlMessage(Kernel& ios, const IOCtlVRequest& ioctlv) V5BulkMessage::V5BulkMessage(Kernel& ios, const IOCtlVRequest& ioctlv) : BulkMessage(ios, ioctlv, ioctlv.GetVector(1)->address) { - length = static_cast(ioctlv.GetVector(1)->size); + length = ioctlv.GetVector(1)->size; endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 18); } V5IntrMessage::V5IntrMessage(Kernel& ios, const IOCtlVRequest& ioctlv) : IntrMessage(ios, ioctlv, ioctlv.GetVector(1)->address) { - length = static_cast(ioctlv.GetVector(1)->size); + length = ioctlv.GetVector(1)->size; endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 14); } @@ -50,7 +50,7 @@ V5IsoMessage::V5IsoMessage(Kernel& ios, const IOCtlVRequest& ioctlv) packet_sizes_addr = ioctlv.GetVector(1)->address; for (size_t i = 0; i < num_packets; ++i) packet_sizes.push_back(Memory::Read_U16(static_cast(packet_sizes_addr + i * sizeof(u16)))); - length = static_cast(ioctlv.GetVector(2)->size); + length = ioctlv.GetVector(2)->size; } } // namespace USB From 4c6ef812913943673ab61295cf9e9c139d9ae366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 2 May 2019 19:01:48 +0200 Subject: [PATCH 3/7] IOS/USB: Verify that isochronous req buffer size is consistent To catch possible bugs. --- Source/Core/Core/IOS/USB/USBV5.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/IOS/USB/USBV5.cpp b/Source/Core/Core/IOS/USB/USBV5.cpp index 0da9448a65..d1d6169b23 100644 --- a/Source/Core/Core/IOS/USB/USBV5.cpp +++ b/Source/Core/Core/IOS/USB/USBV5.cpp @@ -8,6 +8,7 @@ #include #include +#include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/Logging/Log.h" #include "Common/Swap.h" @@ -48,9 +49,16 @@ V5IsoMessage::V5IsoMessage(Kernel& ios, const IOCtlVRequest& ioctlv) num_packets = Memory::Read_U8(ioctlv.in_vectors[0].address + 16); endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 17); packet_sizes_addr = ioctlv.GetVector(1)->address; + u32 total_packet_size = 0; for (size_t i = 0; i < num_packets; ++i) - packet_sizes.push_back(Memory::Read_U16(static_cast(packet_sizes_addr + i * sizeof(u16)))); + { + const u32 packet_size = Memory::Read_U16(static_cast(packet_sizes_addr + i * sizeof(u16))); + packet_sizes.push_back(packet_size); + total_packet_size += packet_size; + } length = ioctlv.GetVector(2)->size; + ASSERT_MSG(IOS_USB, length == total_packet_size, "Wrong buffer size (0x%x != 0x%x)", length, + total_packet_size); } } // namespace USB From d7e23d71f8621faa3b051efff9dd46671b2d4eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 2 May 2019 17:37:43 +0200 Subject: [PATCH 4/7] IOS/VEN: Return -4 when no transfer was cancelled Simple accuracy fix. --- Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp index 4bb28968e3..e969417839 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -105,7 +105,9 @@ s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv) IPCCommandResult USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request) { const u8 endpoint = static_cast(Memory::Read_U32(request.buffer_in + 8)); - GetDeviceById(device.host_id)->CancelTransfer(endpoint); + // IPC_EINVAL (-4) is returned when no transfer was cancelled. + if (GetDeviceById(device.host_id)->CancelTransfer(endpoint) < 0) + return GetDefaultReply(IPC_EINVAL); return GetDefaultReply(IPC_SUCCESS); } From b274a054a919b5edbe8ca2d8391023e2b99ac0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 2 May 2019 18:31:32 +0200 Subject: [PATCH 5/7] IOS/VEN: Read cancel endpoint correctly Fixes an embarrassing bug that made the implementation utterly useless. This fixes Your Shape hanging on shutdown. The game was waiting for an interrupt transfer to be cancelled, and Dolphin wasn't cancelling transfers on the correct endpoint. --- Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp index e969417839..008ab464b3 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -104,7 +104,7 @@ s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv) IPCCommandResult USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request) { - const u8 endpoint = static_cast(Memory::Read_U32(request.buffer_in + 8)); + const u8 endpoint = Memory::Read_U8(request.buffer_in + 8); // IPC_EINVAL (-4) is returned when no transfer was cancelled. if (GetDeviceById(device.host_id)->CancelTransfer(endpoint) < 0) return GetDefaultReply(IPC_EINVAL); From 2b44e1b851175f986ae8f7d51457a4d23fc609d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 4 May 2019 12:23:57 +0200 Subject: [PATCH 6/7] IOS/USB: Fix initial device scan Even though libusb is supposed to be thread-safe, in practice it's not (at least on Windows); getting a list of devices from two different threads can result in libusb crashes. This is easily fixed by waiting for the scan thread to complete scanning instead of running the scan on the CPU thread. This also fixes an issue that I had overlooked in the initial implementation: IOS interfaces such as OH0 are sometimes opened every frame, in which case we were doing a full device scan every single frame on the CPU thread! --- Source/Core/Core/IOS/USB/Host.cpp | 11 +++++++---- Source/Core/Core/IOS/USB/Host.h | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/IOS/USB/Host.cpp b/Source/Core/Core/IOS/USB/Host.cpp index 9b97078cd4..03b9c30c62 100644 --- a/Source/Core/Core/IOS/USB/Host.cpp +++ b/Source/Core/Core/IOS/USB/Host.cpp @@ -45,12 +45,14 @@ USBHost::~USBHost() IPCCommandResult 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()) + if (!m_has_initialised) { + StartThreads(); + // Force a device scan to complete, because some games (including Your Shape) only care + // about the initial device list (in the first GETDEVICECHANGE reply). + m_first_scan_complete_event.Wait(); + m_has_initialised = true; } - StartThreads(); return GetDefaultReply(IPC_SUCCESS); } @@ -117,6 +119,7 @@ bool USBHost::UpdateDevices(const bool always_add_hooks) return false; DetectRemovedDevices(plugged_devices, hooks); DispatchHooks(hooks); + m_first_scan_complete_event.Set(); return true; } diff --git a/Source/Core/Core/IOS/USB/Host.h b/Source/Core/Core/IOS/USB/Host.h index 4555b0f88a..dc37efe4c6 100644 --- a/Source/Core/Core/IOS/USB/Host.h +++ b/Source/Core/Core/IOS/USB/Host.h @@ -15,6 +15,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/Event.h" #include "Common/Flag.h" #include "Core/IOS/Device.h" #include "Core/IOS/IOS.h" @@ -76,5 +77,7 @@ private: // Device scanning thread Common::Flag m_scan_thread_running; std::thread m_scan_thread; + Common::Event m_first_scan_complete_event; + bool m_has_initialised = false; }; } // namespace IOS::HLE::Device From 6dd0fe21f2c5af6a2bad5ec32f46aca2401024e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 2 May 2019 16:33:11 +0200 Subject: [PATCH 7/7] IOS/USB: Claim all interfaces ahead-of-time To avoid having to claim/release interfaces all the time, and having to trigger interface changes from several places, all interfaces are now claimed ahead of time. This commit also makes us avoid changing the active interface when it's not necessary. Changing the active interface has side effects such as resetting the active alternate setting -- which is extremely undesirable because it would require the emulated software to change the alternate setting again, which isn't supposed to be necessary at all. This fixes Your Shape, which submits isochronous transfers on an endpoint that only exists in alt setting 6 right after submitting control transfers (which would have reset to alt setting 0 prior to this fix). --- Source/Core/Core/IOS/USB/Common.h | 10 +- Source/Core/Core/IOS/USB/LibusbDevice.cpp | 126 ++++++++++++--------- Source/Core/Core/IOS/USB/LibusbDevice.h | 8 +- Source/Core/Core/IOS/USB/OH0/OH0.cpp | 4 +- Source/Core/Core/IOS/USB/USBV5.cpp | 2 +- Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp | 2 +- Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp | 5 +- Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp | 5 +- 8 files changed, 101 insertions(+), 61 deletions(-) diff --git a/Source/Core/Core/IOS/USB/Common.h b/Source/Core/Core/IOS/USB/Common.h index 8a6c4edf06..5837d1c640 100644 --- a/Source/Core/Core/IOS/USB/Common.h +++ b/Source/Core/Core/IOS/USB/Common.h @@ -15,6 +15,8 @@ namespace IOS::HLE::USB { +constexpr u8 DEFAULT_CONFIG_NUM = 0; + enum StandardDeviceRequestCodes { REQUEST_GET_DESCRIPTOR = 6, @@ -165,7 +167,13 @@ public: virtual std::vector GetEndpoints(u8 config, u8 interface, u8 alt) const = 0; virtual std::string GetErrorName(int error_code) const; - virtual bool Attach(u8 interface) = 0; + /// Ensure the device is ready to use. + virtual bool Attach() = 0; + /// Ensure the device is ready to use and change the active interface (if needed). + /// + /// This may reset the active alt setting, so prefer using Attach when interface changes + /// are unnecessary (e.g. for control requests). + virtual bool AttachAndChangeInterface(u8 interface) = 0; virtual int CancelTransfer(u8 endpoint) = 0; virtual int ChangeInterface(u8 interface) = 0; virtual int GetNumberOfAltSettings(u8 interface) = 0; diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.cpp b/Source/Core/Core/IOS/USB/LibusbDevice.cpp index 736695cab7..df346ae95b 100644 --- a/Source/Core/Core/IOS/USB/LibusbDevice.cpp +++ b/Source/Core/Core/IOS/USB/LibusbDevice.cpp @@ -42,10 +42,11 @@ LibusbDevice::LibusbDevice(Kernel& ios, libusb_device* device, LibusbDevice::~LibusbDevice() { - if (m_device_attached) - DetachInterface(); if (m_handle != nullptr) + { + ReleaseAllInterfacesForCurrentConfig(); libusb_close(m_handle); + } libusb_unref_device(m_device); } @@ -124,28 +125,39 @@ std::string LibusbDevice::GetErrorName(const int error_code) const return libusb_error_name(error_code); } -bool LibusbDevice::Attach(const u8 interface) +bool LibusbDevice::Attach() { - 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) + if (!m_handle) { - ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to open: %s", m_vid, m_pid, libusb_error_name(ret)); - return 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)); + m_handle = nullptr; + return false; + } } - if (AttachInterface(interface) != 0) + if (ClaimAllInterfaces(DEFAULT_CONFIG_NUM) < 0) return false; m_device_attached = true; return true; } +bool LibusbDevice::AttachAndChangeInterface(const u8 interface) +{ + if (!Attach()) + return false; + + if (interface != m_active_interface) + return ChangeInterface(interface) == 0; + + return true; +} + int LibusbDevice::CancelTransfer(const u8 endpoint) { INFO_LOG(IOS_USB, "[%04x:%04x %d] Cancelling transfers (endpoint 0x%x)", m_vid, m_pid, @@ -159,15 +171,10 @@ int LibusbDevice::CancelTransfer(const u8 endpoint) 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); + m_active_interface = interface; + return 0; } int LibusbDevice::SetAltSetting(const u8 alt_setting) @@ -217,9 +224,13 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr cmd) { INFO_LOG(IOS_USB, "[%04x:%04x %d] REQUEST_SET_CONFIGURATION index=%04x value=%04x", m_vid, m_pid, m_active_interface, cmd->index, cmd->value); + ReleaseAllInterfacesForCurrentConfig(); const int ret = libusb_set_configuration(m_handle, cmd->value); if (ret == 0) + { + ClaimAllInterfaces(cmd->value); m_ios.EnqueueIPCReply(cmd->ios_request, cmd->length); + } return ret; } } @@ -395,50 +406,61 @@ int LibusbDevice::GetNumberOfAltSettings(const u8 interface_number) return m_config_descriptors[0]->Get()->interface[interface_number].num_altsetting; } -int LibusbDevice::AttachInterface(const u8 interface) +template +static int DoForEachInterface(const Configs& configs, u8 config_num, Function action) { - 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)); + int ret = LIBUSB_ERROR_NOT_FOUND; + if (configs.size() <= config_num || !configs[config_num]->IsValid()) return ret; - } - const int r = libusb_claim_interface(m_handle, interface); - if (r < 0) + for (u8 i = 0; i < configs[config_num]->Get()->bNumInterfaces; ++i) { - ERROR_LOG(IOS_USB, "[%04x:%04x] Couldn't claim interface %d: %s", m_vid, m_pid, interface, - libusb_error_name(r)); - return r; + ret = action(i); + if (ret < 0) + break; } - m_active_interface = interface; - return 0; + return ret; } -int LibusbDevice::DetachInterface() +int LibusbDevice::ClaimAllInterfaces(u8 config_num) const { - if (m_handle == nullptr) + const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](u8 i) { + const int ret2 = libusb_detach_kernel_driver(m_handle, i); + if (ret2 < 0 && ret2 != LIBUSB_ERROR_NOT_FOUND && ret2 != LIBUSB_ERROR_NOT_SUPPORTED) + { + ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to detach kernel driver: %s", m_vid, m_pid, + libusb_error_name(ret2)); + return ret2; + } + return libusb_claim_interface(m_handle, i); + }); + if (ret < 0) { - ERROR_LOG(IOS_USB, "[%04x:%04x] Cannot detach without a valid device handle", m_vid, m_pid); - return -1; + ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to claim all interfaces (configuration %u)", m_vid, + m_pid, config_num); } + return ret; +} - 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) +int LibusbDevice::ReleaseAllInterfaces(u8 config_num) const +{ + const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](u8 i) { + return libusb_release_interface(m_handle, i); + }); + if (ret < 0 && ret != LIBUSB_ERROR_NO_DEVICE && ret != LIBUSB_ERROR_NOT_FOUND) { - 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; + ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to release all interfaces (configuration %u)", m_vid, + m_pid, config_num); } - return 0; + return ret; +} + +int LibusbDevice::ReleaseAllInterfacesForCurrentConfig() const +{ + int config_num; + const int get_config_ret = libusb_get_configuration(m_handle, &config_num); + if (get_config_ret < 0) + return get_config_ret; + return ReleaseAllInterfaces(config_num); } LibusbConfigDescriptor::LibusbConfigDescriptor(libusb_device* device, const u8 config_num) diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.h b/Source/Core/Core/IOS/USB/LibusbDevice.h index 6093120ab7..a068a6c9e4 100644 --- a/Source/Core/Core/IOS/USB/LibusbDevice.h +++ b/Source/Core/Core/IOS/USB/LibusbDevice.h @@ -48,7 +48,8 @@ public: std::vector GetInterfaces(u8 config) const override; std::vector GetEndpoints(u8 config, u8 interface, u8 alt) const override; std::string GetErrorName(int error_code) const override; - bool Attach(u8 interface) override; + bool Attach() override; + bool AttachAndChangeInterface(u8 interface) override; int CancelTransfer(u8 endpoint) override; int ChangeInterface(u8 interface) override; int GetNumberOfAltSettings(u8 interface) override; @@ -85,8 +86,9 @@ private: static void CtrlTransferCallback(libusb_transfer* transfer); static void TransferCallback(libusb_transfer* transfer); - int AttachInterface(u8 interface); - int DetachInterface(); + int ClaimAllInterfaces(u8 config_num) const; + int ReleaseAllInterfaces(u8 config_num) const; + int ReleaseAllInterfacesForCurrentConfig() const; }; } // namespace IOS::HLE::USB #endif diff --git a/Source/Core/Core/IOS/USB/OH0/OH0.cpp b/Source/Core/Core/IOS/USB/OH0/OH0.cpp index c0bed0b5e3..603589ae5d 100644 --- a/Source/Core/Core/IOS/USB/OH0/OH0.cpp +++ b/Source/Core/Core/IOS/USB/OH0/OH0.cpp @@ -252,8 +252,10 @@ std::pair OH0::DeviceOpen(const u16 vid, const u16 pid) has_device_with_vid_pid = true; if (m_opened_devices.find(device.second->GetId()) != m_opened_devices.cend() || - !device.second->Attach(0)) + !device.second->Attach()) + { continue; + } m_opened_devices.emplace(device.second->GetId()); return {IPC_SUCCESS, device.second->GetId()}; diff --git a/Source/Core/Core/IOS/USB/USBV5.cpp b/Source/Core/Core/IOS/USB/USBV5.cpp index d1d6169b23..f78e8e6ae7 100644 --- a/Source/Core/Core/IOS/USB/USBV5.cpp +++ b/Source/Core/Core/IOS/USB/USBV5.cpp @@ -140,7 +140,7 @@ IPCCommandResult USBV5ResourceManager::SetAlternateSetting(USBV5Device& device, const IOCtlRequest& request) { const auto host_device = GetDeviceById(device.host_id); - if (!host_device->Attach(device.interface_number)) + if (!host_device->AttachAndChangeInterface(device.interface_number)) return GetDefaultReply(-1); const u8 alt_setting = Memory::Read_U8(request.buffer_in + 2 * sizeof(s32)); diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp index 636dd62a46..08c463f52f 100644 --- a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp @@ -55,7 +55,7 @@ IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request) 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)) + if (!device->Attach()) return GetDefaultReply(IPC_EINVAL); return HandleTransfer(device, request.request, [&, this]() { return SubmitTransfer(*device, request); }); diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp index 54fd2d7884..f453c0cebd 100644 --- a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp @@ -67,7 +67,10 @@ IPCCommandResult USB_HIDv5::IOCtlV(const IOCtlVRequest& request) if (!device) return GetDefaultReply(IPC_EINVAL); auto host_device = GetDeviceById(device->host_id); - host_device->Attach(device->interface_number); + if (request.request == USB::IOCTLV_USBV5_CTRLMSG) + host_device->Attach(); + else + host_device->AttachAndChangeInterface(device->interface_number); return HandleTransfer(host_device, request.request, [&, this]() { return SubmitTransfer(*device, *host_device, request); }); } diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp index 008ab464b3..068ab2924b 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -76,7 +76,10 @@ IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request) if (!device) return GetDefaultReply(IPC_EINVAL); auto host_device = GetDeviceById(device->host_id); - host_device->Attach(device->interface_number); + if (request.request == USB::IOCTLV_USBV5_CTRLMSG) + host_device->Attach(); + else + host_device->AttachAndChangeInterface(device->interface_number); return HandleTransfer(host_device, request.request, [&, this]() { return SubmitTransfer(*host_device, request); }); }