diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp index 955710f176..a61fcc5f84 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -4,6 +4,7 @@ #include "Core/IOS/USB/USB_VEN/VEN.h" +#include #include #include #include @@ -26,6 +27,28 @@ namespace HLE { namespace Device { +namespace +{ +#pragma pack(push, 1) +struct DeviceID +{ + u8 ipc_address_shifted; + u8 index; + u16 number; +}; + +struct DeviceEntry +{ + DeviceID id; + u16 vid; + u16 pid; + u16 number; + u8 interface_number; + u8 num_altsettings; +}; +#pragma pack(pop) +} + USB_VEN::USB_VEN(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name) { } @@ -90,12 +113,14 @@ IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request) if (request.in_vectors.size() + request.io_vectors.size() != s_num_vectors.at(request.request)) return GetDefaultReply(IPC_EINVAL); - const s32 device_id = Memory::Read_U32(request.in_vectors[0].address); - auto device = GetDeviceByIOSID(device_id); - if (!device || !device->Attach(GetInterfaceNumber(device_id))) - return GetDefaultReply(IPC_ENOENT); - return HandleTransfer(device, request.request, - [&, this]() { return SubmitTransfer(*device, request); }); + std::lock_guard lock{m_usbv5_devices_mutex}; + USBV5Device* device = GetUSBV5Device(request.in_vectors[0].address); + if (!device) + return GetDefaultReply(IPC_EINVAL); + 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); }); } default: return GetDefaultReply(IPC_EINVAL); @@ -112,38 +137,37 @@ void USB_VEN::DoState(PointerWrap& p) else m_devicechange_hook_request.reset(); - p.Do(m_device_number); - p.Do(m_ios_ids); - p.Do(m_device_ids); + p.Do(m_usbv5_devices); USBHost::DoState(p); } -std::shared_ptr USB_VEN::GetDeviceByIOSID(const s32 ios_id) const +USB_VEN::USBV5Device* USB_VEN::GetUSBV5Device(u32 in_buffer) { - std::lock_guard lk{m_id_map_mutex}; - const auto iter = m_ios_ids.find(ios_id); - if (iter == m_ios_ids.cend()) + const u8 index = Memory::Read_U8(in_buffer + offsetof(DeviceID, index)); + const u16 number = Memory::Read_U16(in_buffer + offsetof(DeviceID, number)); + + if (index >= m_usbv5_devices.size()) return nullptr; - return GetDeviceById(iter->second); + + USBV5Device* usbv5_device = &m_usbv5_devices[index]; + if (!usbv5_device->in_use || usbv5_device->number != number) + return nullptr; + + return usbv5_device; } -u8 USB_VEN::GetInterfaceNumber(const s32 ios_id) const +IPCCommandResult USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request) { - const s32 id = Common::swap32(ios_id); - DeviceID device_id; - std::memcpy(&device_id, &id, sizeof(id)); - return device_id.interface_plus_1e - 0x1e; -} - -IPCCommandResult USB_VEN::CancelEndpoint(USB::Device& device, const IOCtlRequest& request) -{ - const u8 endpoint = static_cast(Memory::Read_U32(request.buffer_in + 2 * sizeof(s32))); - device.CancelTransfer(endpoint); + const u8 endpoint = static_cast(Memory::Read_U32(request.buffer_in + 8)); + GetDeviceById(device.host_id)->CancelTransfer(endpoint); return GetDefaultReply(IPC_SUCCESS); } IPCCommandResult USB_VEN::GetDeviceChange(const IOCtlRequest& request) { + if (request.buffer_out_size != 0x180 || m_devicechange_hook_request) + return GetDefaultReply(IPC_EINVAL); + std::lock_guard lk{m_devicechange_hook_address_mutex}; m_devicechange_hook_request = std::make_unique(request.address); // On the first call, the reply is sent immediately (instead of on device insertion/removal) @@ -155,14 +179,14 @@ IPCCommandResult USB_VEN::GetDeviceChange(const IOCtlRequest& request) return GetNoReply(); } -IPCCommandResult USB_VEN::GetDeviceInfo(USB::Device& device, const IOCtlRequest& request) +IPCCommandResult USB_VEN::GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request) { - const s32 device_id = Memory::Read_U32(request.buffer_in); if (request.buffer_out == 0 || request.buffer_out_size != 0xc0) return GetDefaultReply(IPC_EINVAL); + const auto host_device = GetDeviceById(device.host_id); const u8 alt_setting = Memory::Read_U8(request.buffer_in + 8); - auto descriptors = device.GetDescriptorsUSBV5(GetInterfaceNumber(device_id), alt_setting); + auto descriptors = host_device->GetDescriptorsUSBV5(device.interface_number, alt_setting); if (descriptors.empty()) return GetDefaultReply(IPC_ENOENT); @@ -171,22 +195,22 @@ IPCCommandResult USB_VEN::GetDeviceInfo(USB::Device& device, const IOCtlRequest& WARN_LOG(IOS_USB, "Buffer is too large. Only the first 172 bytes will be copied."); Memory::Memset(request.buffer_out, 0, request.buffer_out_size); - Memory::Write_U32(device_id, request.buffer_out); + Memory::Write_U32(Memory::Read_U32(request.buffer_in), request.buffer_out); Memory::Write_U32(1, request.buffer_out + 4); Memory::CopyToEmu(request.buffer_out + 20, descriptors.data(), descriptors.size()); return GetDefaultReply(IPC_SUCCESS); } -IPCCommandResult USB_VEN::SetAlternateSetting(USB::Device& device, const IOCtlRequest& request) +IPCCommandResult USB_VEN::SetAlternateSetting(USBV5Device& device, const IOCtlRequest& request) { - const s32 device_id = Memory::Read_U32(request.buffer_in); - if (!device.Attach(GetInterfaceNumber(device_id))) + const auto host_device = GetDeviceById(device.host_id); + if (!host_device->Attach(device.interface_number)) return GetDefaultReply(-1); const u8 alt_setting = Memory::Read_U8(request.buffer_in + 2 * sizeof(s32)); - const bool success = device.SetAltSetting(alt_setting) == 0; + const bool success = host_device->SetAltSetting(alt_setting) == 0; return GetDefaultReply(success ? IPC_SUCCESS : IPC_EINVAL); } @@ -207,15 +231,15 @@ IPCCommandResult USB_VEN::Shutdown(const IOCtlRequest& request) return GetDefaultReply(IPC_SUCCESS); } -IPCCommandResult USB_VEN::SuspendResume(USB::Device& device, const IOCtlRequest& request) +IPCCommandResult USB_VEN::SuspendResume(USBV5Device& device, const IOCtlRequest& request) { - const s32 device_id = Memory::Read_U32(request.buffer_in); - const s32 resumed = Memory::Read_U32(request.buffer_in + 2 * sizeof(s32)); + const auto host_device = GetDeviceById(device.host_id); + const s32 resumed = Memory::Read_U32(request.buffer_in + 8); // Note: this is unimplemented because there's no easy way to do this in a // platform-independant way (libusb does not support power management). - INFO_LOG(IOS_USB, "[%04x:%04x %d] Received %s command", device.GetVid(), device.GetPid(), - GetInterfaceNumber(device_id), resumed == 0 ? "suspend" : "resume"); + INFO_LOG(IOS_USB, "[%04x:%04x %d] Received %s command", host_device->GetVid(), + host_device->GetPid(), device.interface_number, resumed == 0 ? "suspend" : "resume"); return GetDefaultReply(IPC_SUCCESS); } @@ -241,16 +265,17 @@ IPCCommandResult USB_VEN::HandleDeviceIOCtl(const IOCtlRequest& request, Handler if (request.buffer_in == 0 || request.buffer_in_size != 0x20) return GetDefaultReply(IPC_EINVAL); - const s32 device_id = Memory::Read_U32(request.buffer_in); - const auto device = GetDeviceByIOSID(device_id); + std::lock_guard lock{m_usbv5_devices_mutex}; + USBV5Device* device = GetUSBV5Device(request.buffer_in); if (!device) - return GetDefaultReply(IPC_ENOENT); + return GetDefaultReply(IPC_EINVAL); return handler(this, *device, request); } void USB_VEN::OnDeviceChange(const ChangeEvent event, std::shared_ptr device) { - std::lock_guard id_map_lock{m_id_map_mutex}; + std::lock_guard lock{m_usbv5_devices_mutex}; + const u64 host_device_id = device->GetId(); if (event == ChangeEvent::Inserted) { for (const auto& interface : device->GetInterfaces(0)) @@ -258,24 +283,24 @@ void USB_VEN::OnDeviceChange(const ChangeEvent event, std::shared_ptrGetId(); - m_device_ids[device->GetId()].insert(ios_device_id); + it->in_use = true; + it->interface_number = interface.bInterfaceNumber; + it->number = m_current_device_number; + it->host_id = host_device_id; } } else if (event == ChangeEvent::Removed) { - for (const s32 ios_id : m_device_ids[device->GetId()]) - m_ios_ids.erase(ios_id); - m_device_ids.erase(device->GetId()); + for (USBV5Device& entry : m_usbv5_devices) + { + if (entry.host_id == host_device_id) + entry.in_use = false; + } } } @@ -283,7 +308,7 @@ void USB_VEN::OnDeviceChangeEnd() { std::lock_guard lk{m_devicechange_hook_address_mutex}; TriggerDeviceChangeReply(); - ++m_device_number; + ++m_current_device_number; } void USB_VEN::TriggerDeviceChangeReply() @@ -291,40 +316,30 @@ void USB_VEN::TriggerDeviceChangeReply() if (!m_devicechange_hook_request) return; - std::lock_guard id_map_lock{m_id_map_mutex}; + std::lock_guard lock{m_usbv5_devices_mutex}; u8 num_devices = 0; - const size_t max_num = m_devicechange_hook_request->buffer_out_size / sizeof(DeviceEntry); - for (const auto& ios_device : m_ios_ids) + for (auto it = m_usbv5_devices.crbegin(); it != m_usbv5_devices.crend(); ++it) { - if (num_devices >= max_num) - { - WARN_LOG(IOS_USB, "Too many devices (%d ≥ %zu), skipping", num_devices, max_num); - break; - } + const USBV5Device& usbv5_device = *it; + if (!usbv5_device.in_use) + continue; - const s32 ios_device_id = ios_device.first; - const auto device = GetDeviceById(m_ios_ids.at(ios_device_id)); + const auto device = GetDeviceById(usbv5_device.host_id); if (!device) continue; - const u8 interface_number = GetInterfaceNumber(ios_device_id); - - // IOS's device list contains entries of the form: - // e7 XX 00 YY VV VV PP PP 00 YY DD AA - // ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^ ^^^^^ ^^ - // Device ID VID PID ?? See ID Number of alt settings - // - // XX is 1e (for a device plugged in to the left port) + DD (interface number). - // YY is a counter that starts at 21 and is incremented on every device change. - // DD is the interface number (since VEN exposes each interface as a separate device). DeviceEntry entry; - entry.device_id = Common::swap32(ios_device_id); + // The actual value is static_cast(hook_internal_ipc_request >> 8). + // Since we don't actually emulate the IOS kernel and internal IPC, + // just pretend the value is 0xe7 (most common value according to hwtests). + entry.id.ipc_address_shifted = 0xe7; + entry.id.index = static_cast(std::distance(m_usbv5_devices.cbegin(), it.base()) - 1); + entry.id.number = Common::swap16(usbv5_device.number); entry.vid = Common::swap16(device->GetVid()); entry.pid = Common::swap16(device->GetPid()); - entry.unknown = 0x00; - entry.device_number = ios_device_id & 0xff; - entry.interface_number = interface_number; - entry.num_altsettings = device->GetNumberOfAltSettings(interface_number); + entry.number = Common::swap16(usbv5_device.number); + entry.interface_number = usbv5_device.interface_number; + entry.num_altsettings = device->GetNumberOfAltSettings(entry.interface_number); Memory::CopyToEmu(m_devicechange_hook_request->buffer_out + sizeof(entry) * num_devices++, &entry, sizeof(entry)); @@ -332,7 +347,7 @@ void USB_VEN::TriggerDeviceChangeReply() m_ios.EnqueueIPCReply(*m_devicechange_hook_request, num_devices, 0, CoreTiming::FromThread::ANY); m_devicechange_hook_request.reset(); - INFO_LOG(IOS_USB, "%d device(s), including interfaces", num_devices); + INFO_LOG(IOS_USB, "%d USBv5 device(s), including interfaces", num_devices); } } // namespace Device } // namespace HLE diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.h b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h index ed8d71a8d7..1fe96a2b90 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.h +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h @@ -5,10 +5,8 @@ #pragma once #include -#include #include #include -#include #include #include @@ -38,39 +36,18 @@ public: void DoState(PointerWrap& p) override; private: -#pragma pack(push, 1) - struct DeviceID - { - u8 unknown; - u8 interface_plus_1e; - u8 zero; - u8 counter; - }; + struct USBV5Device; + USBV5Device* GetUSBV5Device(u32 in_buffer); - struct DeviceEntry - { - s32 device_id; - u16 vid; - u16 pid; - u8 unknown; - u8 device_number; - u8 interface_number; - u8 num_altsettings; - }; -#pragma pack(pop) - - std::shared_ptr GetDeviceByIOSID(s32 ios_id) const; - u8 GetInterfaceNumber(s32 ios_id) const; - - IPCCommandResult CancelEndpoint(USB::Device& device, const IOCtlRequest& request); + IPCCommandResult CancelEndpoint(USBV5Device& device, const IOCtlRequest& request); IPCCommandResult GetDeviceChange(const IOCtlRequest& request); - IPCCommandResult GetDeviceInfo(USB::Device& device, const IOCtlRequest& request); - IPCCommandResult SetAlternateSetting(USB::Device& device, const IOCtlRequest& request); + IPCCommandResult GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request); + IPCCommandResult SetAlternateSetting(USBV5Device& device, const IOCtlRequest& request); IPCCommandResult Shutdown(const IOCtlRequest& request); - IPCCommandResult SuspendResume(USB::Device& device, const IOCtlRequest& request); + IPCCommandResult SuspendResume(USBV5Device& device, const IOCtlRequest& request); s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request); - using Handler = std::function; + using Handler = std::function; IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler); void OnDeviceChange(ChangeEvent, std::shared_ptr) override; @@ -83,12 +60,18 @@ private: std::mutex m_devicechange_hook_address_mutex; std::unique_ptr m_devicechange_hook_request; - mutable std::mutex m_id_map_mutex; - u8 m_device_number = 0x21; - // IOS device IDs => USB device IDs (one to one) - std::map m_ios_ids; - // USB device IDs => IOS device IDs (one to many, because VEN exposes one device per interface) - std::map> m_device_ids; + // Each interface of a USB device is internally considered as a unique device. + // USBv5 resource managers can handle up to 32 devices/interfaces. + struct USBV5Device + { + bool in_use = false; + u8 interface_number; + u16 number; + u64 host_id; + }; + std::array m_usbv5_devices; + mutable std::mutex m_usbv5_devices_mutex; + u16 m_current_device_number = 0x21; }; } // namespace Device } // namespace HLE