diff --git a/Source/Core/Core/IOS/Device.cpp b/Source/Core/Core/IOS/Device.cpp index c5da4c662c..467ce5cfd1 100644 --- a/Source/Core/Core/IOS/Device.cpp +++ b/Source/Core/Core/IOS/Device.cpp @@ -7,6 +7,7 @@ #include #include +#include "Common/Assert.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" #include "Core/HW/Memmap.h" @@ -77,6 +78,14 @@ IOCtlVRequest::IOCtlVRequest(const u32 address_) : Request(address_) } } +const IOCtlVRequest::IOVector* IOCtlVRequest::GetVector(size_t index) const +{ + _assert_(index < (in_vectors.size() + io_vectors.size())); + if (index < in_vectors.size()) + return &in_vectors[index]; + return &io_vectors[index - in_vectors.size()]; +} + bool IOCtlVRequest::HasNumberOfValidVectors(const size_t in_count, const size_t io_count) const { if (in_vectors.size() != in_count || io_vectors.size() != io_count) diff --git a/Source/Core/Core/IOS/Device.h b/Source/Core/Core/IOS/Device.h index e9191f163c..0723972be4 100644 --- a/Source/Core/Core/IOS/Device.h +++ b/Source/Core/Core/IOS/Device.h @@ -156,6 +156,7 @@ struct IOCtlVRequest final : Request // merging them into a single std::vector would make using the first out vector more complicated. std::vector in_vectors; std::vector io_vectors; + const IOVector* GetVector(size_t index) const; explicit IOCtlVRequest(u32 address); bool HasNumberOfValidVectors(size_t in_count, size_t io_count) const; void Dump(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS, diff --git a/Source/Core/Core/IOS/USB/Common.cpp b/Source/Core/Core/IOS/USB/Common.cpp index a9627b0974..a91ac518b1 100644 --- a/Source/Core/Core/IOS/USB/Common.cpp +++ b/Source/Core/Core/IOS/USB/Common.cpp @@ -6,7 +6,6 @@ #include -#include "Common/Align.h" #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/StringUtil.h" @@ -70,82 +69,26 @@ bool Device::HasClass(const u8 device_class) const }); } -static void CopyToBufferAligned(std::vector* buffer, const void* data, const size_t size) +void DeviceDescriptor::Swap() { - buffer->insert(buffer->end(), static_cast(data), static_cast(data) + size); - const size_t number_of_padding_bytes = Common::AlignUp(size, 4) - size; - buffer->insert(buffer->end(), number_of_padding_bytes, 0); + bcdUSB = Common::swap16(bcdUSB); + idVendor = Common::swap16(idVendor); + idProduct = Common::swap16(idProduct); + bcdDevice = Common::swap16(bcdDevice); } -static void CopyDescriptorToBuffer(std::vector* buffer, DeviceDescriptor descriptor) +void ConfigDescriptor::Swap() { - descriptor.bcdUSB = Common::swap16(descriptor.bcdUSB); - descriptor.idVendor = Common::swap16(descriptor.idVendor); - descriptor.idProduct = Common::swap16(descriptor.idProduct); - descriptor.bcdDevice = Common::swap16(descriptor.bcdDevice); - CopyToBufferAligned(buffer, &descriptor, descriptor.bLength); + wTotalLength = Common::swap16(wTotalLength); } -static void CopyDescriptorToBuffer(std::vector* buffer, ConfigDescriptor descriptor) +void InterfaceDescriptor::Swap() { - descriptor.wTotalLength = Common::swap16(descriptor.wTotalLength); - CopyToBufferAligned(buffer, &descriptor, descriptor.bLength); } -static void CopyDescriptorToBuffer(std::vector* buffer, InterfaceDescriptor descriptor) +void EndpointDescriptor::Swap() { - CopyToBufferAligned(buffer, &descriptor, descriptor.bLength); -} - -static void CopyDescriptorToBuffer(std::vector* buffer, EndpointDescriptor descriptor) -{ - descriptor.wMaxPacketSize = Common::swap16(descriptor.wMaxPacketSize); - // IOS only copies 8 bytes from the endpoint descriptor, regardless of the actual length - CopyToBufferAligned(buffer, &descriptor, sizeof(descriptor)); -} - -std::vector Device::GetDescriptorsUSBV4() const -{ - return GetDescriptors([](const auto& descriptor) { return true; }); -} - -std::vector Device::GetDescriptorsUSBV5(const u8 interface, const u8 alt_setting) const -{ - return GetDescriptors([interface, alt_setting](const auto& descriptor) { - // The USBV5 interfaces present each interface as a different device, - // and the descriptors are filtered by alternate setting. - return descriptor.bInterfaceNumber == interface && descriptor.bAlternateSetting == alt_setting; - }); -} - -std::vector -Device::GetDescriptors(std::function predicate) const -{ - std::vector buffer; - - const auto device_descriptor = GetDeviceDescriptor(); - CopyDescriptorToBuffer(&buffer, device_descriptor); - - const auto configurations = GetConfigurations(); - for (size_t c = 0; c < configurations.size(); ++c) - { - const auto& config_descriptor = configurations[c]; - CopyDescriptorToBuffer(&buffer, config_descriptor); - - const auto interfaces = GetInterfaces(static_cast(c)); - for (size_t i = interfaces.size(); i-- > 0;) - { - const auto& descriptor = interfaces[i]; - if (!predicate(descriptor)) - continue; - - CopyDescriptorToBuffer(&buffer, descriptor); - for (const auto& endpoint_descriptor : GetEndpoints( - static_cast(c), descriptor.bInterfaceNumber, descriptor.bAlternateSetting)) - CopyDescriptorToBuffer(&buffer, endpoint_descriptor); - } - } - return buffer; + wMaxPacketSize = Common::swap16(wMaxPacketSize); } std::string Device::GetErrorName(const int error_code) const diff --git a/Source/Core/Core/IOS/USB/Common.h b/Source/Core/Core/IOS/USB/Common.h index 9efb6630a1..2013716a07 100644 --- a/Source/Core/Core/IOS/USB/Common.h +++ b/Source/Core/Core/IOS/USB/Common.h @@ -44,6 +44,7 @@ constexpr u16 USBHDR(u8 dir, u8 type, u8 recipient, u8 request) struct DeviceDescriptor { + void Swap(); u8 bLength; u8 bDescriptorType; u16 bcdUSB; @@ -62,6 +63,7 @@ struct DeviceDescriptor struct ConfigDescriptor { + void Swap(); u8 bLength; u8 bDescriptorType; u16 wTotalLength; @@ -74,6 +76,7 @@ struct ConfigDescriptor struct InterfaceDescriptor { + void Swap(); u8 bLength; u8 bDescriptorType; u8 bInterfaceNumber; @@ -87,6 +90,7 @@ struct InterfaceDescriptor struct EndpointDescriptor { + void Swap(); u8 bLength; u8 bDescriptorType; u8 bEndpointAddress; @@ -158,8 +162,6 @@ public: u16 GetVid() const; u16 GetPid() const; bool HasClass(u8 device_class) const; - std::vector GetDescriptorsUSBV4() const; - std::vector GetDescriptorsUSBV5(u8 interface, u8 alt_setting) const; virtual DeviceDescriptor GetDeviceDescriptor() const = 0; virtual std::vector GetConfigurations() const = 0; @@ -178,7 +180,6 @@ public: virtual int SubmitTransfer(std::unique_ptr message) = 0; protected: - std::vector GetDescriptors(std::function predicate) const; u64 m_id = 0xFFFFFFFFFFFFFFFF; }; } // namespace USB diff --git a/Source/Core/Core/IOS/USB/USBV5.cpp b/Source/Core/Core/IOS/USB/USBV5.cpp index df81c11014..424a3dc98f 100644 --- a/Source/Core/Core/IOS/USB/USBV5.cpp +++ b/Source/Core/Core/IOS/USB/USBV5.cpp @@ -4,13 +4,15 @@ #include "Core/IOS/USB/USBV5.h" +#include #include -#include -#include +#include -#include "Common/CommonTypes.h" +#include "Common/ChunkFile.h" +#include "Common/Logging/Log.h" +#include "Common/Swap.h" +#include "Core/CoreTiming.h" #include "Core/HW/Memmap.h" -#include "Core/IOS/Device.h" namespace IOS { @@ -19,39 +21,258 @@ namespace HLE namespace USB { V5CtrlMessage::V5CtrlMessage(Kernel& ios, const IOCtlVRequest& ioctlv) - : CtrlMessage(ios, ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 16)) + : CtrlMessage(ios, ioctlv, ioctlv.GetVector(1)->address) { request_type = Memory::Read_U8(ioctlv.in_vectors[0].address + 8); request = Memory::Read_U8(ioctlv.in_vectors[0].address + 9); value = Memory::Read_U16(ioctlv.in_vectors[0].address + 10); index = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); - length = Memory::Read_U16(ioctlv.in_vectors[0].address + 14); + length = static_cast(ioctlv.GetVector(1)->size); } V5BulkMessage::V5BulkMessage(Kernel& ios, const IOCtlVRequest& ioctlv) - : BulkMessage(ios, ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) + : BulkMessage(ios, ioctlv, ioctlv.GetVector(1)->address) { - length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); + length = static_cast(ioctlv.GetVector(1)->size); endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 18); } V5IntrMessage::V5IntrMessage(Kernel& ios, const IOCtlVRequest& ioctlv) - : IntrMessage(ios, ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) + : IntrMessage(ios, ioctlv, ioctlv.GetVector(1)->address) { - length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); + length = static_cast(ioctlv.GetVector(1)->size); endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 14); } V5IsoMessage::V5IsoMessage(Kernel& ios, const IOCtlVRequest& ioctlv) - : IsoMessage(ios, ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) + : IsoMessage(ios, ioctlv, ioctlv.GetVector(2)->address) { num_packets = Memory::Read_U8(ioctlv.in_vectors[0].address + 16); endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 17); - packet_sizes_addr = Memory::Read_U32(ioctlv.in_vectors[0].address + 12); + 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 = std::accumulate(packet_sizes.begin(), packet_sizes.end(), 0); + length = static_cast(ioctlv.GetVector(2)->size); } } // namespace USB + +namespace Device +{ +namespace +{ +#pragma pack(push, 1) +struct DeviceID +{ + u8 reserved; + u8 index; + u16 number; +}; + +struct DeviceEntry +{ + DeviceID id; + u16 vid; + u16 pid; + u16 number; + u8 interface_number; + u8 num_altsettings; +}; +#pragma pack(pop) +} + +USBV5ResourceManager::~USBV5ResourceManager() +{ + StopThreads(); +} + +void USBV5ResourceManager::DoState(PointerWrap& p) +{ + p.Do(m_devicechange_first_call); + u32 hook_address = m_devicechange_hook_request ? m_devicechange_hook_request->address : 0; + p.Do(hook_address); + if (hook_address != 0) + m_devicechange_hook_request = std::make_unique(hook_address); + else + m_devicechange_hook_request.reset(); + + p.Do(m_usbv5_devices); + USBHost::DoState(p); +} + +USBV5ResourceManager::USBV5Device* USBV5ResourceManager::GetUSBV5Device(u32 in_buffer) +{ + 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; + + USBV5Device* usbv5_device = &m_usbv5_devices[index]; + if (!usbv5_device->in_use || usbv5_device->number != number) + return nullptr; + + return usbv5_device; +} + +IPCCommandResult USBV5ResourceManager::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) + if (m_devicechange_first_call) + { + TriggerDeviceChangeReply(); + m_devicechange_first_call = false; + } + return GetNoReply(); +} + +IPCCommandResult USBV5ResourceManager::SetAlternateSetting(USBV5Device& device, + const IOCtlRequest& request) +{ + 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 = host_device->SetAltSetting(alt_setting) == 0; + return GetDefaultReply(success ? IPC_SUCCESS : IPC_EINVAL); +} + +IPCCommandResult USBV5ResourceManager::Shutdown(const IOCtlRequest& request) +{ + if (request.buffer_in != 0 || request.buffer_in_size != 0 || request.buffer_out != 0 || + request.buffer_out_size != 0) + { + return GetDefaultReply(IPC_EINVAL); + } + + std::lock_guard lk{m_devicechange_hook_address_mutex}; + if (m_devicechange_hook_request) + { + m_ios.EnqueueIPCReply(*m_devicechange_hook_request, IPC_SUCCESS); + m_devicechange_hook_request.reset(); + } + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult USBV5ResourceManager::SuspendResume(USBV5Device& device, + const IOCtlRequest& request) +{ + 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", host_device->GetVid(), + host_device->GetPid(), device.interface_number, resumed == 0 ? "suspend" : "resume"); + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult USBV5ResourceManager::HandleDeviceIOCtl(const IOCtlRequest& request, + Handler handler) +{ + if (request.buffer_in == 0 || request.buffer_in_size != 0x20) + return GetDefaultReply(IPC_EINVAL); + + std::lock_guard lock{m_usbv5_devices_mutex}; + USBV5Device* device = GetUSBV5Device(request.buffer_in); + if (!device) + return GetDefaultReply(IPC_EINVAL); + return handler(*device); +} + +void USBV5ResourceManager::OnDeviceChange(const ChangeEvent event, + std::shared_ptr device) +{ + 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)) + { + if (interface.bAlternateSetting != 0) + continue; + + auto it = std::find_if(m_usbv5_devices.rbegin(), m_usbv5_devices.rend(), + [](const USBV5Device& entry) { return !entry.in_use; }); + if (it == m_usbv5_devices.rend()) + return; + + 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 (USBV5Device& entry : m_usbv5_devices) + { + if (entry.host_id == host_device_id) + entry.in_use = false; + } + } +} + +void USBV5ResourceManager::OnDeviceChangeEnd() +{ + std::lock_guard lk{m_devicechange_hook_address_mutex}; + TriggerDeviceChangeReply(); + ++m_current_device_number; +} + +// Must be called with m_devicechange_hook_address_mutex locked +void USBV5ResourceManager::TriggerDeviceChangeReply() +{ + if (!m_devicechange_hook_request) + return; + + std::lock_guard lock{m_usbv5_devices_mutex}; + u8 num_devices = 0; + for (auto it = m_usbv5_devices.crbegin(); it != m_usbv5_devices.crend(); ++it) + { + const USBV5Device& usbv5_device = *it; + if (!usbv5_device.in_use) + continue; + + const auto device = GetDeviceById(usbv5_device.host_id); + if (!device) + continue; + + DeviceEntry entry; + if (HasInterfaceNumberInIDs()) + { + entry.id.reserved = usbv5_device.interface_number; + } + else + { + // 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.reserved = 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.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)); + ++num_devices; + } + + m_ios.EnqueueIPCReply(*m_devicechange_hook_request, num_devices, 0, CoreTiming::FromThread::ANY); + m_devicechange_hook_request.reset(); + INFO_LOG(IOS_USB, "%d USBv5 device(s), including interfaces", num_devices); +} +} // namespace Device } // namespace HLE } // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USBV5.h b/Source/Core/Core/IOS/USB/USBV5.h index 2db6c22110..ceca6bcb71 100644 --- a/Source/Core/Core/IOS/USB/USBV5.h +++ b/Source/Core/Core/IOS/USB/USBV5.h @@ -4,8 +4,18 @@ #pragma once +#include +#include +#include +#include + #include "Common/CommonTypes.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/IOS.h" #include "Core/IOS/USB/Common.h" +#include "Core/IOS/USB/Host.h" + +class PointerWrap; // Used by late USB interfaces for /dev/usb/ven and /dev/usb/hid (since IOS57 which // reorganised the USB modules in IOS). @@ -54,5 +64,54 @@ struct V5IsoMessage final : IsoMessage V5IsoMessage(Kernel& ios, const IOCtlVRequest& cmd_buffer); }; } // namespace USB + +namespace Device +{ +class USBV5ResourceManager : public USBHost +{ +public: + using USBHost::USBHost; + ~USBV5ResourceManager() override; + + IPCCommandResult IOCtl(const IOCtlRequest& request) override = 0; + IPCCommandResult IOCtlV(const IOCtlVRequest& request) override = 0; + + void DoState(PointerWrap& p) override; + +protected: + struct USBV5Device; + USBV5Device* GetUSBV5Device(u32 in_buffer); + + IPCCommandResult GetDeviceChange(const IOCtlRequest& request); + IPCCommandResult SetAlternateSetting(USBV5Device& device, const IOCtlRequest& request); + IPCCommandResult Shutdown(const IOCtlRequest& request); + IPCCommandResult SuspendResume(USBV5Device& device, const IOCtlRequest& request); + + using Handler = std::function; + IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler); + + void OnDeviceChange(ChangeEvent event, std::shared_ptr device) override; + void OnDeviceChangeEnd() override; + void TriggerDeviceChangeReply(); + virtual bool HasInterfaceNumberInIDs() const = 0; + + bool m_devicechange_first_call = true; + std::mutex m_devicechange_hook_address_mutex; + std::unique_ptr m_devicechange_hook_request; + + // 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 = 0; + u16 number = 0; + u64 host_id = 0; + }; + std::array m_usbv5_devices{}; + mutable std::mutex m_usbv5_devices_mutex; + u16 m_current_device_number = 0x21; +}; +} // namespace Device } // namespace HLE } // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp index edcf8b18c3..b0a1606e6c 100644 --- a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp @@ -210,6 +210,40 @@ void USB_HIDv4::TriggerDeviceChangeReply() m_devicechange_hook_request.reset(); } +template +static void CopyDescriptorToBuffer(std::vector* buffer, T descriptor) +{ + const size_t size = sizeof(descriptor); + descriptor.Swap(); + buffer->insert(buffer->end(), reinterpret_cast(&descriptor), + reinterpret_cast(&descriptor) + size); + const size_t number_of_padding_bytes = Common::AlignUp(size, 4) - size; + buffer->insert(buffer->end(), number_of_padding_bytes, 0); +} + +static std::vector GetDescriptors(const USB::Device& device) +{ + std::vector buffer; + + CopyDescriptorToBuffer(&buffer, device.GetDeviceDescriptor()); + const auto configurations = device.GetConfigurations(); + for (size_t c = 0; c < configurations.size(); ++c) + { + CopyDescriptorToBuffer(&buffer, configurations[c]); + const auto interfaces = device.GetInterfaces(static_cast(c)); + for (size_t i = interfaces.size(); i-- > 0;) + { + CopyDescriptorToBuffer(&buffer, interfaces[i]); + for (const auto& endpoint_descriptor : device.GetEndpoints( + static_cast(c), interfaces[i].bInterfaceNumber, interfaces[i].bAlternateSetting)) + { + CopyDescriptorToBuffer(&buffer, endpoint_descriptor); + } + } + } + return buffer; +} + std::vector USB_HIDv4::GetDeviceEntry(const USB::Device& device) const { std::lock_guard id_map_lock{m_id_map_mutex}; @@ -219,7 +253,7 @@ std::vector USB_HIDv4::GetDeviceEntry(const USB::Device& device) const // 4-8 bytes: device ID // the rest of the buffer is device descriptors data std::vector entry(8); - const std::vector descriptors = device.GetDescriptorsUSBV4(); + const std::vector descriptors = GetDescriptors(device); const u32 entry_size = Common::swap32(static_cast(entry.size() + descriptors.size())); const u32 ios_device_id = Common::swap32(m_device_ids.at(device.GetId())); std::memcpy(entry.data(), &entry_size, sizeof(entry_size)); diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp index d51d648c66..98e8fcba5b 100644 --- a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.cpp @@ -4,12 +4,15 @@ #include "Core/IOS/USB/USB_HID/HIDv5.h" -#include +#include +#include +#include +#include +#include "Common/CommonTypes.h" +#include "Common/Logging/Log.h" #include "Core/HW/Memmap.h" -#include "Core/IOS/Device.h" #include "Core/IOS/USB/Common.h" -#include "Core/IOS/USB/USBV5.h" namespace IOS { @@ -17,14 +20,9 @@ namespace HLE { namespace Device { -USB_HIDv5::USB_HIDv5(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name) -{ -} +constexpr u32 USBV5_VERSION = 0x50001; -USB_HIDv5::~USB_HIDv5() -{ - StopThreads(); -} +USB_HIDv5::~USB_HIDv5() = default; IPCCommandResult USB_HIDv5::IOCtl(const IOCtlRequest& request) { @@ -32,32 +30,170 @@ IPCCommandResult USB_HIDv5::IOCtl(const IOCtlRequest& request) switch (request.request) { case USB::IOCTL_USBV5_GETVERSION: - Memory::Write_U32(VERSION, request.buffer_out); - return GetDefaultReply(IPC_SUCCESS); - case USB::IOCTL_USBV5_SHUTDOWN: - if (m_hanging_request) - { - IOCtlRequest hanging_request{m_hanging_request}; - m_ios.EnqueueIPCReply(hanging_request, IPC_SUCCESS); - } + Memory::Write_U32(USBV5_VERSION, request.buffer_out); return GetDefaultReply(IPC_SUCCESS); case USB::IOCTL_USBV5_GETDEVICECHANGE: - if (m_devicechange_replied) - { - m_hanging_request = request.address; - return GetNoReply(); - } - else - { - m_devicechange_replied = true; - return GetDefaultReply(IPC_SUCCESS); - } + return GetDeviceChange(request); + case USB::IOCTL_USBV5_SHUTDOWN: + return Shutdown(request); + case USB::IOCTL_USBV5_GETDEVPARAMS: + return HandleDeviceIOCtl(request, + [&](USBV5Device& device) { return GetDeviceInfo(device, request); }); + case USB::IOCTL_USBV5_ATTACHFINISH: + return GetDefaultReply(IPC_SUCCESS); + case USB::IOCTL_USBV5_SUSPEND_RESUME: + return HandleDeviceIOCtl(request, + [&](USBV5Device& device) { return SuspendResume(device, request); }); + case USB::IOCTL_USBV5_CANCELENDPOINT: + return HandleDeviceIOCtl(request, + [&](USBV5Device& device) { return CancelEndpoint(device, request); }); default: - request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB); + request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR); return GetDefaultReply(IPC_SUCCESS); } } +IPCCommandResult USB_HIDv5::IOCtlV(const IOCtlVRequest& request) +{ + request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB); + switch (request.request) + { + // TODO: HIDv5 seems to be able to queue transfers depending on the transfer length (unlike VEN). + case USB::IOCTLV_USBV5_CTRLMSG: + case USB::IOCTLV_USBV5_INTRMSG: + { + // IOS does not check the number of vectors, but let's do that to avoid out-of-bounds reads. + if (request.in_vectors.size() + request.io_vectors.size() != 2) + return GetDefaultReply(IPC_EINVAL); + + 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(*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) +{ + const u8 value = Memory::Read_U8(request.buffer_in + 8); + u8 endpoint = 0; + switch (value) + { + case 0: + // TODO: cancel all queued control transfers with return code -7022. + endpoint = 0; + break; + case 1: + // TODO: cancel all queued interrupt transfers with return code -7022. + endpoint = m_additional_device_data[&device - m_usbv5_devices.data()].interrupt_in_endpoint; + break; + case 2: + // TODO: cancel all queued interrupt transfers with return code -7022. + endpoint = m_additional_device_data[&device - m_usbv5_devices.data()].interrupt_out_endpoint; + break; + } + + GetDeviceById(device.host_id)->CancelTransfer(endpoint); + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult USB_HIDv5::GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request) +{ + if (request.buffer_out == 0 || request.buffer_out_size != 0x60) + return GetDefaultReply(IPC_EINVAL); + + const std::shared_ptr host_device = GetDeviceById(device.host_id); + const u8 alt_setting = Memory::Read_U8(request.buffer_in + 8); + + Memory::Memset(request.buffer_out, 0, request.buffer_out_size); + Memory::Write_U32(Memory::Read_U32(request.buffer_in), request.buffer_out); + Memory::Write_U32(1, request.buffer_out + 4); + + USB::DeviceDescriptor device_descriptor = host_device->GetDeviceDescriptor(); + device_descriptor.Swap(); + Memory::CopyToEmu(request.buffer_out + 36, &device_descriptor, sizeof(device_descriptor)); + + // Just like VEN, HIDv5 only cares about the first configuration. + USB::ConfigDescriptor config_descriptor = host_device->GetConfigurations()[0]; + config_descriptor.Swap(); + Memory::CopyToEmu(request.buffer_out + 56, &config_descriptor, sizeof(config_descriptor)); + + std::vector interfaces = host_device->GetInterfaces(0); + auto it = std::find_if(interfaces.begin(), interfaces.end(), + [&](const USB::InterfaceDescriptor& interface) { + return interface.bInterfaceNumber == device.interface_number && + interface.bAlternateSetting == alt_setting; + }); + if (it == interfaces.end()) + return GetDefaultReply(IPC_EINVAL); + it->Swap(); + Memory::CopyToEmu(request.buffer_out + 68, &*it, sizeof(*it)); + + auto endpoints = host_device->GetEndpoints(0, it->bInterfaceNumber, it->bAlternateSetting); + for (auto& endpoint : endpoints) + { + constexpr u8 ENDPOINT_INTERRUPT = 0b11; + constexpr u8 ENDPOINT_IN = 0x80; + if (endpoint.bmAttributes == ENDPOINT_INTERRUPT) + { + 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)); + } + } + + return GetDefaultReply(IPC_SUCCESS); +} + +bool USB_HIDv5::ShouldAddDevice(const USB::Device& device) const +{ + // XXX: HIDv5 opens /dev/usb/usb with mode 3 (which is likely HID_CLASS), + // unlike VEN (which opens it with mode 0xff). But is this really correct? + constexpr u8 HID_CLASS = 0x03; + return device.HasClass(HID_CLASS); +} } // namespace Device } // namespace HLE } // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h index 069651ff6e..82709898fa 100644 --- a/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h +++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv5.h @@ -4,11 +4,10 @@ #pragma once -#include - -#include "Common/CommonTypes.h" +#include "Core/IOS/Device.h" #include "Core/IOS/IOS.h" #include "Core/IOS/USB/Host.h" +#include "Core/IOS/USB/USBV5.h" namespace IOS { @@ -16,22 +15,29 @@ namespace HLE { namespace Device { -// Stub implementation that only gets DQX to boot. -class USB_HIDv5 : public USBHost +class USB_HIDv5 final : public USBV5ResourceManager { public: - USB_HIDv5(Kernel& ios, const std::string& device_name); + using USBV5ResourceManager::USBV5ResourceManager; ~USB_HIDv5() override; IPCCommandResult IOCtl(const IOCtlRequest& request) override; + IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; private: - static constexpr u32 VERSION = 0x50001; + 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); - u32 m_hanging_request = 0; - bool m_devicechange_replied = false; + 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 } // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp index 955710f176..8e71f5b6d9 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -4,21 +4,15 @@ #include "Core/IOS/USB/USB_VEN/VEN.h" +#include #include -#include #include #include -#include -#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" #include "Common/Logging/Log.h" -#include "Common/Swap.h" - -#include "Core/CoreTiming.h" #include "Core/HW/Memmap.h" -#include "Core/IOS/Device.h" #include "Core/IOS/USB/Common.h" -#include "Core/IOS/USB/USBV5.h" namespace IOS { @@ -26,14 +20,9 @@ namespace HLE { namespace Device { -USB_VEN::USB_VEN(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name) -{ -} +constexpr u32 USBV5_VERSION = 0x50001; -USB_VEN::~USB_VEN() -{ - StopThreads(); -} +USB_VEN::~USB_VEN() = default; ReturnCode USB_VEN::Open(const OpenRequest& request) { @@ -49,22 +38,26 @@ IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request) switch (request.request) { case USB::IOCTL_USBV5_GETVERSION: - Memory::Write_U32(VERSION, request.buffer_out); + Memory::Write_U32(USBV5_VERSION, request.buffer_out); return GetDefaultReply(IPC_SUCCESS); case USB::IOCTL_USBV5_GETDEVICECHANGE: return GetDeviceChange(request); case USB::IOCTL_USBV5_SHUTDOWN: return Shutdown(request); case USB::IOCTL_USBV5_GETDEVPARAMS: - return HandleDeviceIOCtl(request, &USB_VEN::GetDeviceInfo); + return HandleDeviceIOCtl(request, + [&](USBV5Device& device) { return GetDeviceInfo(device, request); }); case USB::IOCTL_USBV5_ATTACHFINISH: return GetDefaultReply(IPC_SUCCESS); case USB::IOCTL_USBV5_SETALTERNATE: - return HandleDeviceIOCtl(request, &USB_VEN::SetAlternateSetting); + return HandleDeviceIOCtl( + request, [&](USBV5Device& device) { return SetAlternateSetting(device, request); }); case USB::IOCTL_USBV5_SUSPEND_RESUME: - return HandleDeviceIOCtl(request, &USB_VEN::SuspendResume); + return HandleDeviceIOCtl(request, + [&](USBV5Device& device) { return SuspendResume(device, request); }); case USB::IOCTL_USBV5_CANCELENDPOINT: - return HandleDeviceIOCtl(request, &USB_VEN::CancelEndpoint); + return HandleDeviceIOCtl(request, + [&](USBV5Device& device) { return CancelEndpoint(device, request); }); default: request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR); return GetDefaultReply(IPC_SUCCESS); @@ -90,135 +83,20 @@ 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); } } -void USB_VEN::DoState(PointerWrap& p) -{ - p.Do(m_devicechange_first_call); - u32 hook_address = m_devicechange_hook_request ? m_devicechange_hook_request->address : 0; - p.Do(hook_address); - if (hook_address != 0) - m_devicechange_hook_request = std::make_unique(hook_address); - else - m_devicechange_hook_request.reset(); - - p.Do(m_device_number); - p.Do(m_ios_ids); - p.Do(m_device_ids); - USBHost::DoState(p); -} - -std::shared_ptr USB_VEN::GetDeviceByIOSID(const s32 ios_id) const -{ - std::lock_guard lk{m_id_map_mutex}; - const auto iter = m_ios_ids.find(ios_id); - if (iter == m_ios_ids.cend()) - return nullptr; - return GetDeviceById(iter->second); -} - -u8 USB_VEN::GetInterfaceNumber(const s32 ios_id) const -{ - 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); - return GetDefaultReply(IPC_SUCCESS); -} - -IPCCommandResult USB_VEN::GetDeviceChange(const IOCtlRequest& request) -{ - 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) - if (m_devicechange_first_call) - { - TriggerDeviceChangeReply(); - m_devicechange_first_call = false; - } - return GetNoReply(); -} - -IPCCommandResult USB_VEN::GetDeviceInfo(USB::Device& 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 u8 alt_setting = Memory::Read_U8(request.buffer_in + 8); - auto descriptors = device.GetDescriptorsUSBV5(GetInterfaceNumber(device_id), alt_setting); - if (descriptors.empty()) - return GetDefaultReply(IPC_ENOENT); - - descriptors.resize(request.buffer_out_size - 20); - if (descriptors.size() > request.buffer_out_size - 20) - 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(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) -{ - const s32 device_id = Memory::Read_U32(request.buffer_in); - if (!device.Attach(GetInterfaceNumber(device_id))) - return GetDefaultReply(-1); - - const u8 alt_setting = Memory::Read_U8(request.buffer_in + 2 * sizeof(s32)); - - const bool success = device.SetAltSetting(alt_setting) == 0; - return GetDefaultReply(success ? IPC_SUCCESS : IPC_EINVAL); -} - -IPCCommandResult USB_VEN::Shutdown(const IOCtlRequest& request) -{ - if (request.buffer_in != 0 || request.buffer_in_size != 0 || request.buffer_out != 0 || - request.buffer_out_size != 0) - { - return GetDefaultReply(IPC_EINVAL); - } - - std::lock_guard lk{m_devicechange_hook_address_mutex}; - if (m_devicechange_hook_request) - { - m_ios.EnqueueIPCReply(*m_devicechange_hook_request, IPC_SUCCESS); - m_devicechange_hook_request.reset(); - } - return GetDefaultReply(IPC_SUCCESS); -} - -IPCCommandResult USB_VEN::SuspendResume(USB::Device& 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)); - - // 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"); - return GetDefaultReply(IPC_SUCCESS); -} - s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv) { switch (ioctlv.request) @@ -236,103 +114,54 @@ s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv) } } -IPCCommandResult USB_VEN::HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler) +IPCCommandResult USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request) { - if (request.buffer_in == 0 || request.buffer_in_size != 0x20) + 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::GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request) +{ + if (request.buffer_out == 0 || request.buffer_out_size != 0xc0) return GetDefaultReply(IPC_EINVAL); - const s32 device_id = Memory::Read_U32(request.buffer_in); - const auto device = GetDeviceByIOSID(device_id); - if (!device) - return GetDefaultReply(IPC_ENOENT); - return handler(this, *device, request); -} + const std::shared_ptr host_device = GetDeviceById(device.host_id); + const u8 alt_setting = Memory::Read_U8(request.buffer_in + 8); -void USB_VEN::OnDeviceChange(const ChangeEvent event, std::shared_ptr device) -{ - std::lock_guard id_map_lock{m_id_map_mutex}; - if (event == ChangeEvent::Inserted) + Memory::Memset(request.buffer_out, 0, request.buffer_out_size); + Memory::Write_U32(Memory::Read_U32(request.buffer_in), request.buffer_out); + Memory::Write_U32(1, request.buffer_out + 4); + + USB::DeviceDescriptor device_descriptor = host_device->GetDeviceDescriptor(); + device_descriptor.Swap(); + Memory::CopyToEmu(request.buffer_out + 20, &device_descriptor, sizeof(device_descriptor)); + + // VEN only cares about the first configuration. + USB::ConfigDescriptor config_descriptor = host_device->GetConfigurations()[0]; + config_descriptor.Swap(); + Memory::CopyToEmu(request.buffer_out + 40, &config_descriptor, sizeof(config_descriptor)); + + std::vector interfaces = host_device->GetInterfaces(0); + auto it = std::find_if(interfaces.begin(), interfaces.end(), + [&](const USB::InterfaceDescriptor& interface) { + return interface.bInterfaceNumber == device.interface_number && + interface.bAlternateSetting == alt_setting; + }); + if (it == interfaces.end()) + return GetDefaultReply(IPC_EINVAL); + it->Swap(); + Memory::CopyToEmu(request.buffer_out + 52, &*it, sizeof(*it)); + + auto endpoints = host_device->GetEndpoints(0, it->bInterfaceNumber, it->bAlternateSetting); + for (size_t i = 0; i < endpoints.size(); ++i) { - for (const auto& interface : device->GetInterfaces(0)) - { - if (interface.bAlternateSetting != 0) - continue; - - DeviceID id; - id.unknown = 0xe7; - id.interface_plus_1e = interface.bInterfaceNumber + 0x1e; - id.zero = 0x00; - id.counter = m_device_number; - - s32 ios_device_id = 0; - std::memcpy(&ios_device_id, &id, sizeof(id)); - ios_device_id = Common::swap32(ios_device_id); - m_ios_ids[ios_device_id] = device->GetId(); - m_device_ids[device->GetId()].insert(ios_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()); - } -} - -void USB_VEN::OnDeviceChangeEnd() -{ - std::lock_guard lk{m_devicechange_hook_address_mutex}; - TriggerDeviceChangeReply(); - ++m_device_number; -} - -void USB_VEN::TriggerDeviceChangeReply() -{ - if (!m_devicechange_hook_request) - return; - - std::lock_guard id_map_lock{m_id_map_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) - { - if (num_devices >= max_num) - { - WARN_LOG(IOS_USB, "Too many devices (%d ≥ %zu), skipping", num_devices, max_num); - break; - } - - const s32 ios_device_id = ios_device.first; - const auto device = GetDeviceById(m_ios_ids.at(ios_device_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); - 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); - - Memory::CopyToEmu(m_devicechange_hook_request->buffer_out + sizeof(entry) * num_devices++, - &entry, sizeof(entry)); + endpoints[i].Swap(); + Memory::CopyToEmu(request.buffer_out + 64 + 8 * static_cast(i), &endpoints[i], + sizeof(endpoints[i])); } - 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); + return GetDefaultReply(IPC_SUCCESS); } } // 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..8205b0ca2d 100644 --- a/Source/Core/Core/IOS/USB/USB_VEN/VEN.h +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h @@ -4,20 +4,10 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include - -#include "Common/CommonTypes.h" #include "Core/IOS/Device.h" #include "Core/IOS/IOS.h" #include "Core/IOS/USB/Host.h" - -class PointerWrap; +#include "Core/IOS/USB/USBV5.h" namespace IOS { @@ -25,70 +15,22 @@ namespace HLE { namespace Device { -class USB_VEN final : public USBHost +class USB_VEN final : public USBV5ResourceManager { public: - USB_VEN(Kernel& ios, const std::string& device_name); + using USBV5ResourceManager::USBV5ResourceManager; ~USB_VEN() override; ReturnCode Open(const OpenRequest& request) override; IPCCommandResult IOCtl(const IOCtlRequest& request) override; IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - void DoState(PointerWrap& p) override; - private: -#pragma pack(push, 1) - struct DeviceID - { - u8 unknown; - u8 interface_plus_1e; - u8 zero; - u8 counter; - }; + IPCCommandResult CancelEndpoint(USBV5Device& device, const IOCtlRequest& request); + IPCCommandResult GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request); - 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 GetDeviceChange(const IOCtlRequest& request); - IPCCommandResult GetDeviceInfo(USB::Device& device, const IOCtlRequest& request); - IPCCommandResult SetAlternateSetting(USB::Device& device, const IOCtlRequest& request); - IPCCommandResult Shutdown(const IOCtlRequest& request); - IPCCommandResult SuspendResume(USB::Device& device, const IOCtlRequest& request); - s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request); - - using Handler = std::function; - IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler); - - void OnDeviceChange(ChangeEvent, std::shared_ptr) override; - void OnDeviceChangeEnd() override; - void TriggerDeviceChangeReply(); - - static constexpr u32 VERSION = 0x50001; - - bool m_devicechange_first_call = true; - 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; + s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv); + bool HasInterfaceNumberInIDs() const override { return false; } }; } // namespace Device } // namespace HLE