From b63b6111b3dbbafc3248ff00e41e356d6a090cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 13 Nov 2016 14:23:29 +0100 Subject: [PATCH] IOS: Implement USB_VEN (/dev/usb/ven) --- Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/Core.vcxproj | 6 +- Source/Core/Core/Core.vcxproj.filters | 14 +- Source/Core/Core/IOS/IPC.cpp | 4 +- Source/Core/Core/IOS/USB/USBV5.cpp | 55 ++++ Source/Core/Core/IOS/USB/USBV5.h | 58 ++++ Source/Core/Core/IOS/USB/USB_VEN.cpp | 83 ------ Source/Core/Core/IOS/USB/USB_VEN.h | 46 ---- Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp | 333 +++++++++++++++++++++++ Source/Core/Core/IOS/USB/USB_VEN/VEN.h | 95 +++++++ 10 files changed, 559 insertions(+), 138 deletions(-) create mode 100644 Source/Core/Core/IOS/USB/USBV5.cpp create mode 100644 Source/Core/Core/IOS/USB/USBV5.h delete mode 100644 Source/Core/Core/IOS/USB/USB_VEN.cpp delete mode 100644 Source/Core/Core/IOS/USB/USB_VEN.h create mode 100644 Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp create mode 100644 Source/Core/Core/IOS/USB/USB_VEN/VEN.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 594608b377..166eb158bf 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -158,10 +158,11 @@ set(SRCS ActionReplay.cpp IOS/USB/OH0/OH0.cpp IOS/USB/OH0/OH0Device.cpp IOS/USB/USB_HID/HIDv4.cpp + IOS/USB/USB_VEN/VEN.cpp IOS/USB/USBV0.cpp IOS/USB/USBV4.cpp + IOS/USB/USBV5.cpp IOS/USB/USB_KBD.cpp - IOS/USB/USB_VEN.cpp IOS/USB/Bluetooth/BTBase.cpp IOS/USB/Bluetooth/BTEmu.cpp IOS/USB/Bluetooth/BTStub.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 7983948be3..dc6bcd4a36 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -195,10 +195,11 @@ + + - @@ -427,10 +428,11 @@ + + - diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 3b9ad3503a..109608d2bb 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -788,16 +788,19 @@ IOS\USB + + IOS\USB + IOS\USB IOS\USB - + IOS\USB - + IOS\USB @@ -1383,16 +1386,19 @@ IOS\USB + + IOS\USB + IOS\USB IOS\USB - + IOS\USB - + IOS\USB diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 0db81b2efc..1580791a80 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -53,7 +53,7 @@ #include "Core/IOS/USB/OH0/OH0Device.h" #include "Core/IOS/USB/USB_HID/HIDv4.h" #include "Core/IOS/USB/USB_KBD.h" -#include "Core/IOS/USB/USB_VEN.h" +#include "Core/IOS/USB/USB_VEN/VEN.h" #include "Core/IOS/WFS/WFSI.h" #include "Core/IOS/WFS/WFSSRV.h" @@ -507,12 +507,12 @@ void Reinit() AddDevice("/dev/net/ip/top"); AddDevice("/dev/net/ssl"); AddDevice("/dev/usb/kbd"); - AddDevice("/dev/usb/ven"); AddDevice("/dev/sdio/slot0"); AddDevice("/dev/sdio/slot1"); AddDevice("/dev/usb/hid"); AddDevice("/dev/usb/oh0"); AddDevice("/dev/usb/oh1"); + AddDevice("/dev/usb/ven"); AddDevice("/dev/usb/wfssrv"); AddDevice("/dev/wfsi"); } diff --git a/Source/Core/Core/IOS/USB/USBV5.cpp b/Source/Core/Core/IOS/USB/USBV5.cpp new file mode 100644 index 0000000000..bf4fd09399 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV5.cpp @@ -0,0 +1,55 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/HW/Memmap.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/USB/USBV5.h" + +namespace IOS +{ +namespace HLE +{ +namespace USB +{ +V5CtrlMessage::V5CtrlMessage(const IOCtlVRequest& ioctlv) + : CtrlMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 16)) +{ + 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); +} + +V5BulkMessage::V5BulkMessage(const IOCtlVRequest& ioctlv) + : BulkMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) +{ + length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); + endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 18); +} + +V5IntrMessage::V5IntrMessage(const IOCtlVRequest& ioctlv) + : IntrMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) +{ + length = Memory::Read_U16(ioctlv.in_vectors[0].address + 12); + endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address + 14); +} + +V5IsoMessage::V5IsoMessage(const IOCtlVRequest& ioctlv) + : IsoMessage(ioctlv, Memory::Read_U32(ioctlv.in_vectors[0].address + 8)) +{ + 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); + 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); +} +} // namespace USB +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USBV5.h b/Source/Core/Core/IOS/USB/USBV5.h new file mode 100644 index 0000000000..871d0e7856 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USBV5.h @@ -0,0 +1,58 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" +#include "Core/IOS/USB/Common.h" + +// Used by late USB interfaces for /dev/usb/ven and /dev/usb/hid (since IOS57 which +// reorganised the USB modules in IOS). + +namespace IOS +{ +namespace HLE +{ +struct IOCtlRequest; + +namespace USB +{ +enum V5Requests +{ + IOCTL_USBV5_GETVERSION = 0, + IOCTL_USBV5_GETDEVICECHANGE = 1, + IOCTL_USBV5_SHUTDOWN = 2, + IOCTL_USBV5_GETDEVPARAMS = 3, + IOCTL_USBV5_ATTACHFINISH = 6, + IOCTL_USBV5_SETALTERNATE = 7, + IOCTL_USBV5_SUSPEND_RESUME = 16, + IOCTL_USBV5_CANCELENDPOINT = 17, + IOCTLV_USBV5_CTRLMSG = 18, + IOCTLV_USBV5_INTRMSG = 19, + IOCTLV_USBV5_ISOMSG = 20, + IOCTLV_USBV5_BULKMSG = 21 +}; + +struct V5CtrlMessage final : CtrlMessage +{ + explicit V5CtrlMessage(const IOCtlVRequest& ioctlv); +}; + +struct V5BulkMessage final : BulkMessage +{ + explicit V5BulkMessage(const IOCtlVRequest& ioctlv); +}; + +struct V5IntrMessage final : IntrMessage +{ + explicit V5IntrMessage(const IOCtlVRequest& ioctlv); +}; + +struct V5IsoMessage final : IsoMessage +{ + explicit V5IsoMessage(const IOCtlVRequest& cmd_buffer); +}; +} // namespace USB +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN.cpp b/Source/Core/Core/IOS/USB/USB_VEN.cpp deleted file mode 100644 index 811ef83443..0000000000 --- a/Source/Core/Core/IOS/USB/USB_VEN.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "Core/IOS/USB/USB_VEN.h" -#include "Common/Logging/Log.h" -#include "Core/HW/Memmap.h" - -namespace IOS -{ -namespace HLE -{ -namespace Device -{ -USB_VEN::USB_VEN(u32 device_id, const std::string& device_name) : Device(device_id, device_name) -{ -} - -IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request) -{ - request.Dump(GetDeviceName()); - return GetNoReply(); -} - -IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request) -{ - request.Log(GetDeviceName(), LogTypes::OSHLE); - - IPCCommandResult reply = GetDefaultReply(IPC_SUCCESS); - switch (request.request) - { - case USBV5_IOCTL_GETVERSION: - Memory::Write_U32(0x50001, request.buffer_out); - reply = GetDefaultReply(IPC_SUCCESS); - break; - - case USBV5_IOCTL_GETDEVICECHANGE: - { - // sent on change - static bool firstcall = true; - if (firstcall) - { - reply = GetDefaultReply(IPC_SUCCESS); - firstcall = false; - } - - // num devices - reply = GetDefaultReply(0); - return reply; - } - break; - - case USBV5_IOCTL_ATTACHFINISH: - reply = GetDefaultReply(IPC_SUCCESS); - break; - - case USBV5_IOCTL_SUSPEND_RESUME: - DEBUG_LOG(OSHLE, "Device: %i Resumed: %i", Memory::Read_U32(request.buffer_in), - Memory::Read_U32(request.buffer_in + 4)); - reply = GetDefaultReply(IPC_SUCCESS); - break; - - case USBV5_IOCTL_GETDEVPARAMS: - { - s32 device = Memory::Read_U32(request.buffer_in); - u32 unk = Memory::Read_U32(request.buffer_in + 4); - - DEBUG_LOG(OSHLE, "USBV5_IOCTL_GETDEVPARAMS device: %i unk: %i", device, unk); - - Memory::Write_U32(0, request.buffer_out); - - reply = GetDefaultReply(IPC_SUCCESS); - } - break; - - default: - request.Log(GetDeviceName(), LogTypes::OSHLE, LogTypes::LDEBUG); - } - return reply; -} -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN.h b/Source/Core/Core/IOS/USB/USB_VEN.h deleted file mode 100644 index c52b67cf32..0000000000 --- a/Source/Core/Core/IOS/USB/USB_VEN.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include - -#include "Common/CommonTypes.h" -#include "Core/IOS/Device.h" -#include "Core/IOS/IPC.h" - -namespace IOS -{ -namespace HLE -{ -namespace Device -{ -class USB_VEN final : public Device -{ -public: - USB_VEN(u32 device_id, const std::string& device_name); - - IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - IPCCommandResult IOCtl(const IOCtlRequest& request) override; - -private: - enum USBIOCtl - { - USBV5_IOCTL_GETVERSION = 0, - USBV5_IOCTL_GETDEVICECHANGE = 1, - USBV5_IOCTL_SHUTDOWN = 2, - USBV5_IOCTL_GETDEVPARAMS = 3, - USBV5_IOCTL_ATTACHFINISH = 6, - USBV5_IOCTL_SETALTERNATE = 7, - USBV5_IOCTL_SUSPEND_RESUME = 16, - USBV5_IOCTL_CANCELENDPOINT = 17, - USBV5_IOCTL_CTRLMSG = 18, - USBV5_IOCTL_INTRMSG = 19, - USBV5_IOCTL_ISOMSG = 20, - USBV5_IOCTL_BULKMSG = 21 - }; -}; -} // 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 new file mode 100644 index 0000000000..a8171fcedb --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.cpp @@ -0,0 +1,333 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Common/ChunkFile.h" +#include "Common/CommonFuncs.h" +#include "Common/Logging/Log.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" +#include "Core/IOS/USB/USB_VEN/VEN.h" + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +USB_VEN::USB_VEN(u32 device_id, const std::string& device_name) : USBHost(device_id, device_name) +{ +} + +USB_VEN::~USB_VEN() +{ + StopThreads(); +} + +ReturnCode USB_VEN::Open(const OpenRequest& request) +{ + const u32 ios_major_version = GetVersion(); + if (ios_major_version != 57 && ios_major_version != 58 && ios_major_version != 59) + return IPC_ENOENT; + return USBHost::Open(request); +} + +IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request) +{ + request.Log(GetDeviceName(), LogTypes::IOS_USB); + switch (request.request) + { + case USB::IOCTL_USBV5_GETVERSION: + Memory::Write_U32(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); + case USB::IOCTL_USBV5_ATTACHFINISH: + return GetDefaultReply(IPC_SUCCESS); + case USB::IOCTL_USBV5_SETALTERNATE: + return HandleDeviceIOCtl(request, &USB_VEN::SetAlternateSetting); + case USB::IOCTL_USBV5_SUSPEND_RESUME: + return HandleDeviceIOCtl(request, &USB_VEN::SuspendResume); + case USB::IOCTL_USBV5_CANCELENDPOINT: + return HandleDeviceIOCtl(request, &USB_VEN::CancelEndpoint); + default: + request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR); + return GetDefaultReply(IPC_SUCCESS); + } +} + +IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request) +{ + static const std::map s_num_vectors = { + {USB::IOCTLV_USBV5_CTRLMSG, 2}, + {USB::IOCTLV_USBV5_INTRMSG, 2}, + {USB::IOCTLV_USBV5_BULKMSG, 2}, + {USB::IOCTLV_USBV5_ISOMSG, 4}, + }; + + switch (request.request) + { + case USB::IOCTLV_USBV5_CTRLMSG: + case USB::IOCTLV_USBV5_INTRMSG: + case USB::IOCTLV_USBV5_BULKMSG: + case USB::IOCTLV_USBV5_ISOMSG: + { + 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); }); + } + 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) + { + EnqueueReply(*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) + { + case USB::IOCTLV_USBV5_CTRLMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + case USB::IOCTLV_USBV5_INTRMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + case USB::IOCTLV_USBV5_BULKMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + case USB::IOCTLV_USBV5_ISOMSG: + return device.SubmitTransfer(std::make_unique(ioctlv)); + default: + return IPC_EINVAL; + } +} + +IPCCommandResult USB_VEN::HandleDeviceIOCtl(const IOCtlRequest& request, Handler 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); + if (!device) + return GetDefaultReply(IPC_ENOENT); + 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}; + if (event == ChangeEvent::Inserted) + { + 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)); + } + + EnqueueReply(*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); +} +} // namespace Device +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/USB/USB_VEN/VEN.h b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h new file mode 100644 index 0000000000..b35ea15384 --- /dev/null +++ b/Source/Core/Core/IOS/USB/USB_VEN/VEN.h @@ -0,0 +1,95 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/IPC.h" +#include "Core/IOS/USB/Host.h" + +class PointerWrap; + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +class USB_VEN final : public USBHost +{ +public: + USB_VEN(u32 device_id, const std::string& device_name); + ~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; + }; + + 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; +}; +} // namespace Device +} // namespace HLE +} // namespace IOS