IOS: Implement OH0 (/dev/usb/oh0)

This commit is contained in:
Léo Lam 2016-11-13 14:23:09 +01:00
parent c8a6dc6c23
commit ee188a1d5a
13 changed files with 646 additions and 3 deletions

View file

@ -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

View file

@ -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" />

View file

@ -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>

View file

@ -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
{

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,