From ef8b3cb96054df790cdb6b5703b5517a67f2953f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 2 Nov 2017 19:11:06 +0100 Subject: [PATCH] USB_VEN: Construct device IDs properly Fix the device ID struct to reflect the actual structure used by IOS. It turns out that offset 2 is the internal device index. The reason that field seemed to be "0x1e - interface_number" is that IOS only keeps track of 32 devices and always looks for free entries from the end of the internal array. With each USB interface being exposed as a separate USBv5 device, "0x1e - interface_number" was mostly correct... but wrong! We also made the assumption that the interface number can be identified from just a USBV5 device ID, which is definitely not true. VEN (and HID) keep track of the interface number in the internal struct instead of "reconstructing" it from the device ID (which is normally not possible if we were generating IDs correctly) This commit fixes all of these inaccuracies. --- Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp | 179 ++++++++++++----------- Source/Core/Core/IOS/USB/USB_VEN/VEN.h | 55 +++---- 2 files changed, 116 insertions(+), 118 deletions(-) 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