diff --git a/Source/Core/Core/IOS/USB/Common.h b/Source/Core/Core/IOS/USB/Common.h index 58a164adc1..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, @@ -127,14 +129,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 +145,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; @@ -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/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 diff --git a/Source/Core/Core/IOS/USB/LibusbDevice.cpp b/Source/Core/Core/IOS/USB/LibusbDevice.cpp index 0821cfacbe..df346ae95b 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" @@ -41,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); } @@ -123,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, @@ -158,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) @@ -184,11 +192,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,9 +222,15 @@ 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); + 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; } } @@ -230,6 +252,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 +269,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 +286,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; @@ -375,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/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..f78e8e6ae7 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" @@ -31,14 +32,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); } @@ -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)))); - length = static_cast(ioctlv.GetVector(2)->size); + { + 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 @@ -132,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 4bb28968e3..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); }); } @@ -104,8 +107,10 @@ 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); + 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); return GetDefaultReply(IPC_SUCCESS); }