IOS: Implement OH0 (/dev/usb/oh0)
This commit is contained in:
parent
c8a6dc6c23
commit
ee188a1d5a
|
@ -155,6 +155,8 @@ set(SRCS ActionReplay.cpp
|
|||
IOS/STM/STM.cpp
|
||||
IOS/USB/Common.cpp
|
||||
IOS/USB/Host.cpp
|
||||
IOS/USB/OH0/OH0.cpp
|
||||
IOS/USB/OH0/OH0Device.cpp
|
||||
IOS/USB/USBV0.cpp
|
||||
IOS/USB/USB_KBD.cpp
|
||||
IOS/USB/USB_VEN.cpp
|
||||
|
|
|
@ -192,6 +192,8 @@
|
|||
<ClCompile Include="IOS\USB\Host.cpp">
|
||||
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\USB\OH0\OH0.cpp" />
|
||||
<ClCompile Include="IOS\USB\OH0\OH0Device.cpp" />
|
||||
<ClCompile Include="IOS\USB\USBV0.cpp" />
|
||||
<ClCompile Include="IOS\USB\USB_HIDv4.cpp">
|
||||
<!--
|
||||
|
@ -427,6 +429,8 @@
|
|||
<ClInclude Include="IOS\USB\Common.h" />
|
||||
<ClInclude Include="IOS\USB\LibusbDevice.h" />
|
||||
<ClInclude Include="IOS\USB\Host.h" />
|
||||
<ClInclude Include="IOS\USB\OH0\OH0.h" />
|
||||
<ClInclude Include="IOS\USB\OH0\OH0Device.h" />
|
||||
<ClInclude Include="IOS\USB\USBV0.h" />
|
||||
<ClInclude Include="IOS\USB\USB_HIDv4.h" />
|
||||
<ClInclude Include="IOS\USB\USB_KBD.h" />
|
||||
|
|
|
@ -779,6 +779,12 @@
|
|||
<ClCompile Include="IOS\USB\Host.cpp">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\USB\OH0\OH0.cpp">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\USB\OH0\OH0Device.cpp">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\USB\USBV0.cpp">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1365,6 +1371,12 @@
|
|||
<ClInclude Include="IOS\USB\Host.h">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\USB\OH0\OH0.h">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\USB\OH0\OH0Device.h">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\USB\USBV0.h">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -76,6 +76,16 @@ bool IOCtlVRequest::HasInputVectorWithAddress(const u32 vector_address) const
|
|||
[&](const auto& in_vector) { return in_vector.address == vector_address; });
|
||||
}
|
||||
|
||||
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)
|
||||
return false;
|
||||
|
||||
auto IsValidVector = [](const auto& vector) { return vector.size == 0 || vector.address != 0; };
|
||||
return std::all_of(in_vectors.begin(), in_vectors.end(), IsValidVector) &&
|
||||
std::all_of(io_vectors.begin(), io_vectors.end(), IsValidVector);
|
||||
}
|
||||
|
||||
void IOCtlRequest::Log(const std::string& device_name, LogTypes::LOG_TYPE type,
|
||||
LogTypes::LOG_LEVELS verbosity) const
|
||||
{
|
||||
|
|
|
@ -42,6 +42,7 @@ enum ReturnCode : s32
|
|||
FS_EDIRDEPTH = -116, // Max directory depth exceeded
|
||||
FS_EBUSY = -118, // Resource busy
|
||||
IPC_EESEXHAUSTED = -1016, // Max of 2 ES handles exceeded
|
||||
USB_ECANCELED = -7022, // USB OH0 insertion hook cancelled
|
||||
};
|
||||
|
||||
struct Request
|
||||
|
@ -122,6 +123,7 @@ struct IOCtlVRequest final : Request
|
|||
std::vector<IOVector> io_vectors;
|
||||
explicit IOCtlVRequest(u32 address);
|
||||
bool HasInputVectorWithAddress(u32 vector_address) const;
|
||||
bool HasNumberOfValidVectors(size_t in_count, size_t io_count) const;
|
||||
void Dump(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
|
||||
LogTypes::LOG_LEVELS level = LogTypes::LINFO) const;
|
||||
void DumpUnknown(const std::string& description, LogTypes::LOG_TYPE type = LogTypes::IOS,
|
||||
|
@ -137,6 +139,7 @@ public:
|
|||
{
|
||||
Static, // Devices which appear in s_device_map.
|
||||
FileIO, // FileIO devices which are created dynamically.
|
||||
OH0, // OH0 child devices which are created dynamically.
|
||||
};
|
||||
|
||||
Device(u32 device_id, const std::string& device_name, DeviceType type = DeviceType::Static);
|
||||
|
|
|
@ -49,6 +49,8 @@
|
|||
#include "Core/IOS/STM/STM.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTReal.h"
|
||||
#include "Core/IOS/USB/OH0/OH0.h"
|
||||
#include "Core/IOS/USB/OH0/OH0Device.h"
|
||||
#include "Core/IOS/USB/USB_KBD.h"
|
||||
#include "Core/IOS/USB/USB_VEN.h"
|
||||
#include "Core/IOS/WFS/WFSI.h"
|
||||
|
@ -516,6 +518,7 @@ void Reinit()
|
|||
#else
|
||||
AddDevice<Device::Stub>("/dev/usb/hid");
|
||||
#endif
|
||||
AddDevice<Device::OH0>("/dev/usb/oh0");
|
||||
AddDevice<Device::Stub>("/dev/usb/oh1");
|
||||
AddDevice<Device::WFSSRV>("/dev/usb/wfssrv");
|
||||
AddDevice<Device::WFSI>("/dev/wfsi");
|
||||
|
@ -657,6 +660,10 @@ void DoState(PointerWrap& p)
|
|||
s_fdmap[i] = std::make_shared<Device::FileIO>(i, "");
|
||||
s_fdmap[i]->DoState(p);
|
||||
break;
|
||||
case Device::Device::DeviceType::OH0:
|
||||
s_fdmap[i] = std::make_shared<Device::OH0Device>(i, "");
|
||||
s_fdmap[i]->DoState(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -723,6 +730,10 @@ static s32 OpenDevice(const OpenRequest& request)
|
|||
if (!device)
|
||||
return IPC_EESEXHAUSTED;
|
||||
}
|
||||
else if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
|
||||
{
|
||||
device = std::make_shared<Device::OH0Device>(new_fd, request.path);
|
||||
}
|
||||
else if (request.path.find("/dev/") == 0)
|
||||
{
|
||||
device = GetDeviceByName(request.path);
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/IOS/USB/Common.h"
|
||||
#include "Core/IOS/USB/OH0/OH0.h"
|
||||
#include "Core/IOS/USB/USBV0.h"
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
namespace HLE
|
||||
{
|
||||
namespace Device
|
||||
{
|
||||
OH0::OH0(u32 device_id, const std::string& device_name) : USBHost(device_id, device_name)
|
||||
{
|
||||
}
|
||||
|
||||
OH0::~OH0()
|
||||
{
|
||||
StopThreads();
|
||||
}
|
||||
|
||||
ReturnCode OH0::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_EACCES;
|
||||
return USBHost::Open(request);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::IOCtl(const IOCtlRequest& request)
|
||||
{
|
||||
request.Log(GetDeviceName(), LogTypes::IOS_USB);
|
||||
switch (request.request)
|
||||
{
|
||||
case USB::IOCTL_USBV0_GETRHDESCA:
|
||||
return GetRhDesca(request);
|
||||
case USB::IOCTL_USBV0_CANCEL_INSERT_HOOK:
|
||||
return CancelInsertionHook(request);
|
||||
default:
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::IOCtlV(const IOCtlVRequest& request)
|
||||
{
|
||||
INFO_LOG(IOS_USB, "/dev/usb/oh0 - IOCtlV %u", request.request);
|
||||
switch (request.request)
|
||||
{
|
||||
case USB::IOCTLV_USBV0_GETDEVLIST:
|
||||
return GetDeviceList(request);
|
||||
case USB::IOCTLV_USBV0_GETRHPORTSTATUS:
|
||||
return GetRhPortStatus(request);
|
||||
case USB::IOCTLV_USBV0_SETRHPORTSTATUS:
|
||||
return SetRhPortStatus(request);
|
||||
case USB::IOCTLV_USBV0_DEVINSERTHOOK:
|
||||
return RegisterInsertionHook(request);
|
||||
case USB::IOCTLV_USBV0_DEVICECLASSCHANGE:
|
||||
return RegisterClassChangeHook(request);
|
||||
case USB::IOCTLV_USBV0_DEVINSERTHOOKID:
|
||||
return RegisterInsertionHookWithID(request);
|
||||
default:
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void OH0::DoState(PointerWrap& p)
|
||||
{
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
Core::DisplayMessage("It is suggested that you unplug and replug all connected USB devices.",
|
||||
5000);
|
||||
Core::DisplayMessage("If USB doesn't work properly, an emulation reset may be needed.", 5000);
|
||||
}
|
||||
p.Do(m_insertion_hooks);
|
||||
p.Do(m_removal_hooks);
|
||||
p.Do(m_opened_devices);
|
||||
USBHost::DoState(p);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::CancelInsertionHook(const IOCtlRequest& request)
|
||||
{
|
||||
if (!request.buffer_in || request.buffer_in_size != 4)
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
// IOS assigns random IDs, but ours are simply the VID + PID (see RegisterInsertionHookWithID)
|
||||
TriggerHook(m_insertion_hooks,
|
||||
{Memory::Read_U16(request.buffer_in), Memory::Read_U16(request.buffer_in + 2)},
|
||||
USB_ECANCELED);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::GetDeviceList(const IOCtlVRequest& request) const
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(2, 2))
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
const u8 max_entries_count = Memory::Read_U8(request.in_vectors[0].address);
|
||||
if (request.io_vectors[1].size != max_entries_count * sizeof(DeviceEntry))
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
const u8 interface_class = Memory::Read_U8(request.in_vectors[1].address);
|
||||
u8 entries_count = 0;
|
||||
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||
for (const auto& device : m_devices)
|
||||
{
|
||||
if (entries_count >= max_entries_count)
|
||||
break;
|
||||
if (!device.second->HasClass(interface_class))
|
||||
continue;
|
||||
|
||||
DeviceEntry entry;
|
||||
entry.unknown = 0;
|
||||
entry.vid = Common::swap16(device.second->GetVid());
|
||||
entry.pid = Common::swap16(device.second->GetPid());
|
||||
Memory::CopyToEmu(request.io_vectors[1].address + 8 * entries_count++, &entry, 8);
|
||||
}
|
||||
Memory::Write_U8(entries_count, request.io_vectors[0].address);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::GetRhDesca(const IOCtlRequest& request) const
|
||||
{
|
||||
if (!request.buffer_out || request.buffer_out_size != 4)
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
// Based on a hardware test, this ioctl seems to return a constant value
|
||||
Memory::Write_U32(0x02000302, request.buffer_out);
|
||||
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LWARNING);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::GetRhPortStatus(const IOCtlVRequest& request) const
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(1, 1))
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
ERROR_LOG(IOS_USB, "Unimplemented IOCtlV: IOCTLV_USBV0_GETRHPORTSTATUS");
|
||||
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::SetRhPortStatus(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(2, 0))
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
ERROR_LOG(IOS_USB, "Unimplemented IOCtlV: IOCTLV_USBV0_SETRHPORTSTATUS");
|
||||
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LERROR);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::RegisterRemovalHook(const u64 device_id, const IOCtlRequest& request)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{m_hooks_mutex};
|
||||
// IOS only allows a single device removal hook.
|
||||
if (m_removal_hooks.find(device_id) != m_removal_hooks.end())
|
||||
return GetDefaultReply(IPC_EEXIST);
|
||||
m_removal_hooks.insert({device_id, request.address});
|
||||
return GetNoReply();
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::RegisterInsertionHook(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(2, 0))
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
const u16 vid = Memory::Read_U16(request.in_vectors[0].address);
|
||||
const u16 pid = Memory::Read_U16(request.in_vectors[1].address);
|
||||
if (HasDeviceWithVidPid(vid, pid))
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
|
||||
std::lock_guard<std::mutex> lock{m_hooks_mutex};
|
||||
// TODO: figure out whether IOS allows more than one hook.
|
||||
m_insertion_hooks[{vid, pid}] = request.address;
|
||||
return GetNoReply();
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::RegisterInsertionHookWithID(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(3, 1))
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
|
||||
std::lock_guard<std::mutex> lock{m_hooks_mutex};
|
||||
const u16 vid = Memory::Read_U16(request.in_vectors[0].address);
|
||||
const u16 pid = Memory::Read_U16(request.in_vectors[1].address);
|
||||
const bool trigger_only_for_new_device = Memory::Read_U8(request.in_vectors[2].address) == 1;
|
||||
if (!trigger_only_for_new_device && HasDeviceWithVidPid(vid, pid))
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
// TODO: figure out whether IOS allows more than one hook.
|
||||
m_insertion_hooks.insert({{vid, pid}, request.address});
|
||||
// The output vector is overwritten with an ID to use with ioctl 31 for cancelling the hook.
|
||||
Memory::Write_U32(vid << 16 | pid, request.io_vectors[0].address);
|
||||
return GetNoReply();
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::RegisterClassChangeHook(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(1, 0))
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
WARN_LOG(IOS_USB, "Unimplemented IOCtlV: USB::IOCTLV_USBV0_DEVICECLASSCHANGE (no reply)");
|
||||
request.Dump(GetDeviceName(), LogTypes::IOS_USB, LogTypes::LWARNING);
|
||||
return GetNoReply();
|
||||
}
|
||||
|
||||
bool OH0::HasDeviceWithVidPid(const u16 vid, const u16 pid) const
|
||||
{
|
||||
return std::any_of(m_devices.begin(), m_devices.end(), [=](const auto& device) {
|
||||
return device.second->GetVid() == vid && device.second->GetPid() == pid;
|
||||
});
|
||||
}
|
||||
|
||||
void OH0::OnDeviceChange(const ChangeEvent event, std::shared_ptr<USB::Device> device)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||
if (event == ChangeEvent::Inserted)
|
||||
TriggerHook(m_insertion_hooks, {device->GetVid(), device->GetPid()}, IPC_SUCCESS);
|
||||
else if (event == ChangeEvent::Removed)
|
||||
TriggerHook(m_removal_hooks, device->GetId(), IPC_SUCCESS);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void OH0::TriggerHook(std::map<T, u32>& hooks, T value, const ReturnCode return_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk{m_hooks_mutex};
|
||||
const auto hook = hooks.find(value);
|
||||
if (hook == hooks.end())
|
||||
return;
|
||||
EnqueueReply(Request{hook->second}, return_value, 0, CoreTiming::FromThread::ANY);
|
||||
hooks.erase(hook);
|
||||
}
|
||||
|
||||
std::pair<ReturnCode, u64> OH0::DeviceOpen(const u16 vid, const u16 pid)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_devices_mutex);
|
||||
|
||||
bool has_device_with_vid_pid = false;
|
||||
for (const auto& device : m_devices)
|
||||
{
|
||||
if (device.second->GetVid() != vid || device.second->GetPid() != pid)
|
||||
continue;
|
||||
has_device_with_vid_pid = true;
|
||||
|
||||
if (m_opened_devices.find(device.second->GetId()) != m_opened_devices.cend() ||
|
||||
!device.second->Attach(0))
|
||||
continue;
|
||||
|
||||
m_opened_devices.emplace(device.second->GetId());
|
||||
return {IPC_SUCCESS, device.second->GetId()};
|
||||
}
|
||||
// IOS doesn't allow opening the same device more than once (IPC_EEXIST)
|
||||
return {has_device_with_vid_pid ? IPC_EEXIST : IPC_ENOENT, 0};
|
||||
}
|
||||
|
||||
void OH0::DeviceClose(const u64 device_id)
|
||||
{
|
||||
TriggerHook(m_removal_hooks, device_id, IPC_ENOENT);
|
||||
m_opened_devices.erase(device_id);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::DeviceIOCtl(const u64 device_id, const IOCtlRequest& request)
|
||||
{
|
||||
const auto device = GetDeviceById(device_id);
|
||||
if (!device)
|
||||
return GetDefaultReply(IPC_ENOENT);
|
||||
|
||||
switch (request.request)
|
||||
{
|
||||
case USB::IOCTL_USBV0_DEVREMOVALHOOK:
|
||||
return RegisterRemovalHook(device_id, request);
|
||||
case USB::IOCTL_USBV0_SUSPENDDEV:
|
||||
case USB::IOCTL_USBV0_RESUMEDEV:
|
||||
// Unimplemented because libusb doesn't do power management.
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
case USB::IOCTL_USBV0_RESET_DEVICE:
|
||||
TriggerHook(m_removal_hooks, device_id, IPC_SUCCESS);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
default:
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
IPCCommandResult OH0::DeviceIOCtlV(const u64 device_id, const IOCtlVRequest& request)
|
||||
{
|
||||
const auto device = GetDeviceById(device_id);
|
||||
if (!device)
|
||||
return GetDefaultReply(IPC_ENOENT);
|
||||
|
||||
switch (request.request)
|
||||
{
|
||||
case USB::IOCTLV_USBV0_CTRLMSG:
|
||||
case USB::IOCTLV_USBV0_BLKMSG:
|
||||
case USB::IOCTLV_USBV0_LBLKMSG:
|
||||
case USB::IOCTLV_USBV0_INTRMSG:
|
||||
case USB::IOCTLV_USBV0_ISOMSG:
|
||||
return HandleTransfer(device, request.request,
|
||||
[&, this]() { return SubmitTransfer(*device, request); });
|
||||
case USB::IOCTLV_USBV0_UNKNOWN_32:
|
||||
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
default:
|
||||
return GetDefaultReply(IPC_EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
s32 OH0::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv)
|
||||
{
|
||||
switch (ioctlv.request)
|
||||
{
|
||||
case USB::IOCTLV_USBV0_CTRLMSG:
|
||||
if (!ioctlv.HasNumberOfValidVectors(6, 1) ||
|
||||
Common::swap16(Memory::Read_U16(ioctlv.in_vectors[4].address)) != ioctlv.io_vectors[0].size)
|
||||
return IPC_EINVAL;
|
||||
return device.SubmitTransfer(std::make_unique<USB::V0CtrlMessage>(ioctlv));
|
||||
|
||||
case USB::IOCTLV_USBV0_BLKMSG:
|
||||
case USB::IOCTLV_USBV0_LBLKMSG:
|
||||
if (!ioctlv.HasNumberOfValidVectors(2, 1) ||
|
||||
Memory::Read_U16(ioctlv.in_vectors[1].address) != ioctlv.io_vectors[0].size)
|
||||
return IPC_EINVAL;
|
||||
return device.SubmitTransfer(
|
||||
std::make_unique<USB::V0BulkMessage>(ioctlv, ioctlv.request == USB::IOCTLV_USBV0_LBLKMSG));
|
||||
|
||||
case USB::IOCTLV_USBV0_INTRMSG:
|
||||
if (!ioctlv.HasNumberOfValidVectors(2, 1) ||
|
||||
Memory::Read_U16(ioctlv.in_vectors[1].address) != ioctlv.io_vectors[0].size)
|
||||
return IPC_EINVAL;
|
||||
return device.SubmitTransfer(std::make_unique<USB::V0IntrMessage>(ioctlv));
|
||||
|
||||
case USB::IOCTLV_USBV0_ISOMSG:
|
||||
if (!ioctlv.HasNumberOfValidVectors(3, 2))
|
||||
return IPC_EINVAL;
|
||||
return device.SubmitTransfer(std::make_unique<USB::V0IsoMessage>(ioctlv));
|
||||
|
||||
default:
|
||||
return IPC_EINVAL;
|
||||
}
|
||||
}
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/IOS/Device.h"
|
||||
#include "Core/IOS/USB/Host.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
namespace HLE
|
||||
{
|
||||
namespace USB
|
||||
{
|
||||
struct DeviceInfo
|
||||
{
|
||||
u16 vid;
|
||||
u16 pid;
|
||||
bool operator<(const DeviceInfo& other) const { return vid < other.vid; }
|
||||
};
|
||||
} // namespace USB
|
||||
|
||||
namespace Device
|
||||
{
|
||||
// /dev/usb/oh0
|
||||
class OH0 final : public USBHost
|
||||
{
|
||||
public:
|
||||
OH0(u32 device_id, const std::string& device_name);
|
||||
~OH0() override;
|
||||
|
||||
ReturnCode Open(const OpenRequest& request) override;
|
||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||
|
||||
std::pair<ReturnCode, u64> DeviceOpen(u16 vid, u16 pid);
|
||||
void DeviceClose(u64 device_id);
|
||||
IPCCommandResult DeviceIOCtl(u64 device_id, const IOCtlRequest& request);
|
||||
IPCCommandResult DeviceIOCtlV(u64 device_id, const IOCtlVRequest& request);
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
private:
|
||||
IPCCommandResult CancelInsertionHook(const IOCtlRequest& request);
|
||||
IPCCommandResult GetDeviceList(const IOCtlVRequest& request) const;
|
||||
IPCCommandResult GetRhDesca(const IOCtlRequest& request) const;
|
||||
IPCCommandResult GetRhPortStatus(const IOCtlVRequest& request) const;
|
||||
IPCCommandResult SetRhPortStatus(const IOCtlVRequest& request);
|
||||
IPCCommandResult RegisterRemovalHook(u64 device_id, const IOCtlRequest& request);
|
||||
IPCCommandResult RegisterInsertionHook(const IOCtlVRequest& request);
|
||||
IPCCommandResult RegisterInsertionHookWithID(const IOCtlVRequest& request);
|
||||
IPCCommandResult RegisterClassChangeHook(const IOCtlVRequest& request);
|
||||
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request);
|
||||
|
||||
bool HasDeviceWithVidPid(u16 vid, u16 pid) const;
|
||||
void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> device) override;
|
||||
template <typename T>
|
||||
void TriggerHook(std::map<T, u32>& hooks, T value, ReturnCode return_value);
|
||||
|
||||
struct DeviceEntry
|
||||
{
|
||||
u32 unknown;
|
||||
u16 vid;
|
||||
u16 pid;
|
||||
};
|
||||
static_assert(sizeof(DeviceEntry) == 8, "sizeof(DeviceEntry) must be 8");
|
||||
|
||||
// Device info (VID, PID) → command address for pending hook requests
|
||||
std::map<USB::DeviceInfo, u32> m_insertion_hooks;
|
||||
std::map<u64, u32> m_removal_hooks;
|
||||
std::set<u64> m_opened_devices;
|
||||
std::mutex m_hooks_mutex;
|
||||
};
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/IOS/IPC.h"
|
||||
#include "Core/IOS/USB/OH0/OH0.h"
|
||||
#include "Core/IOS/USB/OH0/OH0Device.h"
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
namespace HLE
|
||||
{
|
||||
namespace Device
|
||||
{
|
||||
static void GetVidPidFromDevicePath(const std::string& device_path, u16& vid, u16& pid)
|
||||
{
|
||||
std::stringstream stream{device_path};
|
||||
std::string segment;
|
||||
std::vector<std::string> list;
|
||||
while (std::getline(stream, segment, '/'))
|
||||
if (!segment.empty())
|
||||
list.push_back(segment);
|
||||
|
||||
if (list.size() != 5)
|
||||
return;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::hex << list[3];
|
||||
ss >> vid;
|
||||
ss.clear();
|
||||
ss << std::hex << list[4];
|
||||
ss >> pid;
|
||||
}
|
||||
|
||||
OH0Device::OH0Device(u32 id, const std::string& name) : Device(id, name, DeviceType::OH0)
|
||||
{
|
||||
if (!name.empty())
|
||||
GetVidPidFromDevicePath(name, m_vid, m_pid);
|
||||
}
|
||||
|
||||
void OH0Device::DoState(PointerWrap& p)
|
||||
{
|
||||
m_oh0 = std::static_pointer_cast<OH0>(GetDeviceByName("/dev/usb/oh0"));
|
||||
p.Do(m_name);
|
||||
p.Do(m_vid);
|
||||
p.Do(m_pid);
|
||||
p.Do(m_device_id);
|
||||
}
|
||||
|
||||
ReturnCode OH0Device::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;
|
||||
|
||||
if (m_vid == 0 && m_pid == 0)
|
||||
return IPC_ENOENT;
|
||||
|
||||
m_oh0 = std::static_pointer_cast<OH0>(GetDeviceByName("/dev/usb/oh0"));
|
||||
|
||||
ReturnCode return_code;
|
||||
std::tie(return_code, m_device_id) = m_oh0->DeviceOpen(m_vid, m_pid);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
void OH0Device::Close()
|
||||
{
|
||||
m_oh0->DeviceClose(m_device_id);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0Device::IOCtl(const IOCtlRequest& request)
|
||||
{
|
||||
return m_oh0->DeviceIOCtl(m_device_id, request);
|
||||
}
|
||||
|
||||
IPCCommandResult OH0Device::IOCtlV(const IOCtlVRequest& request)
|
||||
{
|
||||
return m_oh0->DeviceIOCtlV(m_device_id, request);
|
||||
}
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/IOS/Device.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
namespace HLE
|
||||
{
|
||||
namespace Device
|
||||
{
|
||||
class OH0;
|
||||
class OH0Device final : public Device
|
||||
{
|
||||
public:
|
||||
OH0Device(u32 device_id, const std::string& device_name);
|
||||
|
||||
ReturnCode Open(const OpenRequest& request) override;
|
||||
void Close() override;
|
||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<OH0> m_oh0;
|
||||
u16 m_vid = 0;
|
||||
u16 m_pid = 0;
|
||||
u64 m_device_id = 0;
|
||||
};
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -42,6 +42,17 @@ V0IntrMessage::V0IntrMessage(const IOCtlVRequest& ioctlv)
|
|||
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address);
|
||||
length = Memory::Read_U16(ioctlv.in_vectors[1].address);
|
||||
}
|
||||
|
||||
V0IsoMessage::V0IsoMessage(const IOCtlVRequest& ioctlv)
|
||||
: IsoMessage(ioctlv, ioctlv.io_vectors[1].address)
|
||||
{
|
||||
endpoint = Memory::Read_U8(ioctlv.in_vectors[0].address);
|
||||
length = Memory::Read_U16(ioctlv.in_vectors[1].address);
|
||||
num_packets = Memory::Read_U8(ioctlv.in_vectors[2].address);
|
||||
packet_sizes_addr = ioctlv.io_vectors[0].address;
|
||||
for (size_t i = 0; i < num_packets; ++i)
|
||||
packet_sizes.push_back(Memory::Read_U16(static_cast<u32>(packet_sizes_addr + i * sizeof(u16))));
|
||||
}
|
||||
} // namespace USB
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/IOS/USB/Common.h"
|
||||
|
||||
|
@ -24,6 +22,21 @@ enum V0Requests
|
|||
IOCTLV_USBV0_CTRLMSG = 0,
|
||||
IOCTLV_USBV0_BLKMSG = 1,
|
||||
IOCTLV_USBV0_INTRMSG = 2,
|
||||
IOCTL_USBV0_SUSPENDDEV = 5,
|
||||
IOCTL_USBV0_RESUMEDEV = 6,
|
||||
IOCTLV_USBV0_ISOMSG = 9,
|
||||
IOCTLV_USBV0_LBLKMSG = 10,
|
||||
IOCTLV_USBV0_GETDEVLIST = 12,
|
||||
IOCTL_USBV0_GETRHDESCA = 15,
|
||||
IOCTLV_USBV0_GETRHPORTSTATUS = 20,
|
||||
IOCTLV_USBV0_SETRHPORTSTATUS = 25,
|
||||
IOCTL_USBV0_DEVREMOVALHOOK = 26,
|
||||
IOCTLV_USBV0_DEVINSERTHOOK = 27,
|
||||
IOCTLV_USBV0_DEVICECLASSCHANGE = 28,
|
||||
IOCTL_USBV0_RESET_DEVICE = 29,
|
||||
IOCTLV_USBV0_DEVINSERTHOOKID = 30,
|
||||
IOCTL_USBV0_CANCEL_INSERT_HOOK = 31,
|
||||
IOCTLV_USBV0_UNKNOWN_32 = 32,
|
||||
};
|
||||
|
||||
struct V0CtrlMessage final : CtrlMessage
|
||||
|
@ -40,6 +53,11 @@ struct V0IntrMessage final : IntrMessage
|
|||
{
|
||||
explicit V0IntrMessage(const IOCtlVRequest& ioctlv);
|
||||
};
|
||||
|
||||
struct V0IsoMessage final : IsoMessage
|
||||
{
|
||||
explicit V0IsoMessage(const IOCtlVRequest& ioctlv);
|
||||
};
|
||||
} // namespace USB
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
||||
|
|
|
@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
|||
static std::thread g_save_thread;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
static const u32 STATE_VERSION = 73; // Last changed in PR 4651
|
||||
static const u32 STATE_VERSION = 74; // Last changed in PR 4408
|
||||
|
||||
// Maps savestate versions to Dolphin versions.
|
||||
// Versions after 42 don't need to be added to this list,
|
||||
|
|
Loading…
Reference in New Issue