USB: Move common USBv5 IOS code to prepare for HIDv5
USBv5 IOS resource managers share most of their code. Some ioctls are even completely the same! So let's separate the common code from the VEN specific stuff to make HIDv5 easier to implement.
This commit is contained in:
parent
6bb03d900c
commit
180ad8076c
|
@ -4,13 +4,16 @@
|
|||
|
||||
#include "Core/IOS/USB/USBV5.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
{
|
||||
|
@ -53,5 +56,234 @@ V5IsoMessage::V5IsoMessage(Kernel& ios, const IOCtlVRequest& ioctlv)
|
|||
length = std::accumulate(packet_sizes.begin(), packet_sizes.end(), 0);
|
||||
}
|
||||
} // namespace USB
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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<IOCtlRequest>(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<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||
m_devicechange_hook_request = std::make_unique<IOCtlRequest>(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<std::mutex> 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);
|
||||
}
|
||||
|
||||
s32 USBV5ResourceManager::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv)
|
||||
{
|
||||
switch (ioctlv.request)
|
||||
{
|
||||
case USB::IOCTLV_USBV5_CTRLMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5CtrlMessage>(m_ios, ioctlv));
|
||||
case USB::IOCTLV_USBV5_INTRMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5IntrMessage>(m_ios, ioctlv));
|
||||
case USB::IOCTLV_USBV5_BULKMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5BulkMessage>(m_ios, ioctlv));
|
||||
case USB::IOCTLV_USBV5_ISOMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5IsoMessage>(m_ios, ioctlv));
|
||||
default:
|
||||
return IPC_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
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<std::mutex> 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<USB::Device> device)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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;
|
||||
// The actual value is static_cast<u8>(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<u8>(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
|
||||
|
|
|
@ -4,8 +4,18 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#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);
|
||||
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request);
|
||||
|
||||
using Handler = std::function<IPCCommandResult(USBV5Device&)>;
|
||||
IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler);
|
||||
|
||||
void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> device) override;
|
||||
void OnDeviceChangeEnd() override;
|
||||
void TriggerDeviceChangeReply();
|
||||
|
||||
bool m_devicechange_first_call = true;
|
||||
std::mutex m_devicechange_hook_address_mutex;
|
||||
std::unique_ptr<IOCtlRequest> 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<USBV5Device, 32> m_usbv5_devices{};
|
||||
mutable std::mutex m_usbv5_devices_mutex;
|
||||
u16 m_current_device_number = 0x21;
|
||||
};
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
||||
|
|
|
@ -6,20 +6,13 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#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
|
||||
{
|
||||
|
@ -27,38 +20,9 @@ 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)
|
||||
}
|
||||
|
||||
constexpr u32 USBV5_VERSION = 0x50001;
|
||||
|
||||
USB_VEN::USB_VEN(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name)
|
||||
{
|
||||
}
|
||||
|
||||
USB_VEN::~USB_VEN()
|
||||
{
|
||||
StopThreads();
|
||||
}
|
||||
USB_VEN::~USB_VEN() = default;
|
||||
|
||||
ReturnCode USB_VEN::Open(const OpenRequest& request)
|
||||
{
|
||||
|
@ -81,15 +45,17 @@ IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request)
|
|||
case USB::IOCTL_USBV5_SHUTDOWN:
|
||||
return Shutdown(request);
|
||||
case USB::IOCTL_USBV5_GETDEVPARAMS:
|
||||
return HandleDeviceIOCtl(request, &USB_VEN::GetDeviceInfo);
|
||||
return HandleDeviceIOCtl(request, [&](auto& 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,
|
||||
[&](auto& device) { return SetAlternateSetting(device, request); });
|
||||
case USB::IOCTL_USBV5_SUSPEND_RESUME:
|
||||
return HandleDeviceIOCtl(request, &USB_VEN::SuspendResume);
|
||||
return HandleDeviceIOCtl(request, [&](auto& device) { return SuspendResume(device, request); });
|
||||
case USB::IOCTL_USBV5_CANCELENDPOINT:
|
||||
return HandleDeviceIOCtl(request, &USB_VEN::CancelEndpoint);
|
||||
return HandleDeviceIOCtl(request,
|
||||
[&](auto& device) { return CancelEndpoint(device, request); });
|
||||
default:
|
||||
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
|
@ -129,35 +95,6 @@ IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request)
|
|||
}
|
||||
}
|
||||
|
||||
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<IOCtlRequest>(hook_address);
|
||||
else
|
||||
m_devicechange_hook_request.reset();
|
||||
|
||||
p.Do(m_usbv5_devices);
|
||||
USBHost::DoState(p);
|
||||
}
|
||||
|
||||
USB_VEN::USBV5Device* USB_VEN::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 USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request)
|
||||
{
|
||||
const u8 endpoint = static_cast<u8>(Memory::Read_U32(request.buffer_in + 8));
|
||||
|
@ -165,22 +102,6 @@ IPCCommandResult USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest
|
|||
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<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||
m_devicechange_hook_request = std::make_unique<IOCtlRequest>(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(USBV5Device& device, const IOCtlRequest& request)
|
||||
{
|
||||
if (request.buffer_out == 0 || request.buffer_out_size != 0xc0)
|
||||
|
@ -222,154 +143,6 @@ IPCCommandResult USB_VEN::GetDeviceInfo(USBV5Device& device, const IOCtlRequest&
|
|||
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
IPCCommandResult USB_VEN::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 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<std::mutex> 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(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);
|
||||
}
|
||||
|
||||
s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv)
|
||||
{
|
||||
switch (ioctlv.request)
|
||||
{
|
||||
case USB::IOCTLV_USBV5_CTRLMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5CtrlMessage>(m_ios, ioctlv));
|
||||
case USB::IOCTLV_USBV5_INTRMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5IntrMessage>(m_ios, ioctlv));
|
||||
case USB::IOCTLV_USBV5_BULKMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5BulkMessage>(m_ios, ioctlv));
|
||||
case USB::IOCTLV_USBV5_ISOMSG:
|
||||
return device.SubmitTransfer(std::make_unique<USB::V5IsoMessage>(m_ios, 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);
|
||||
|
||||
std::lock_guard<std::mutex> lock{m_usbv5_devices_mutex};
|
||||
USBV5Device* device = GetUSBV5Device(request.buffer_in);
|
||||
if (!device)
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
return handler(this, *device, request);
|
||||
}
|
||||
|
||||
void USB_VEN::OnDeviceChange(const ChangeEvent event, std::shared_ptr<USB::Device> device)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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 USB_VEN::OnDeviceChangeEnd()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk{m_devicechange_hook_address_mutex};
|
||||
TriggerDeviceChangeReply();
|
||||
++m_current_device_number;
|
||||
}
|
||||
|
||||
void USB_VEN::TriggerDeviceChangeReply()
|
||||
{
|
||||
if (!m_devicechange_hook_request)
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> 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;
|
||||
// The actual value is static_cast<u8>(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<u8>(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));
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -4,18 +4,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
{
|
||||
|
@ -23,53 +15,19 @@ 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:
|
||||
struct USBV5Device;
|
||||
USBV5Device* GetUSBV5Device(u32 in_buffer);
|
||||
|
||||
IPCCommandResult CancelEndpoint(USBV5Device& device, const IOCtlRequest& request);
|
||||
IPCCommandResult GetDeviceChange(const IOCtlRequest& request);
|
||||
IPCCommandResult GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request);
|
||||
IPCCommandResult SetAlternateSetting(USBV5Device& device, const IOCtlRequest& request);
|
||||
IPCCommandResult Shutdown(const IOCtlRequest& request);
|
||||
IPCCommandResult SuspendResume(USBV5Device& device, const IOCtlRequest& request);
|
||||
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request);
|
||||
|
||||
using Handler = std::function<IPCCommandResult(USB_VEN*, USBV5Device&, const IOCtlRequest&)>;
|
||||
IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler);
|
||||
|
||||
void OnDeviceChange(ChangeEvent, std::shared_ptr<USB::Device>) override;
|
||||
void OnDeviceChangeEnd() override;
|
||||
void TriggerDeviceChangeReply();
|
||||
|
||||
bool m_devicechange_first_call = true;
|
||||
std::mutex m_devicechange_hook_address_mutex;
|
||||
std::unique_ptr<IOCtlRequest> 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;
|
||||
u16 number;
|
||||
u64 host_id;
|
||||
};
|
||||
std::array<USBV5Device, 32> m_usbv5_devices;
|
||||
mutable std::mutex m_usbv5_devices_mutex;
|
||||
u16 m_current_device_number = 0x21;
|
||||
};
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
|
|
Loading…
Reference in New Issue