diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h
index ce46bcb8e2..3ef07acbf1 100644
--- a/Source/Core/Common/Logging/Log.h
+++ b/Source/Core/Common/Logging/Log.h
@@ -31,7 +31,6 @@ enum LOG_TYPE
IOS_DI,
IOS_ES,
IOS_FILEIO,
- IOS_HID,
IOS_NET,
IOS_SD,
IOS_SSL,
diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp
index 4cb786095b..964a8df973 100644
--- a/Source/Core/Common/Logging/LogManager.cpp
+++ b/Source/Core/Common/Logging/LogManager.cpp
@@ -65,7 +65,6 @@ LogManager::LogManager()
m_Log[LogTypes::IOS_DI] = new LogContainer("IOS_DI", "IOS - Drive Interface");
m_Log[LogTypes::IOS_ES] = new LogContainer("IOS_ES", "IOS - ETicket Services");
m_Log[LogTypes::IOS_FILEIO] = new LogContainer("IOS_FILEIO", "IOS - FileIO");
- m_Log[LogTypes::IOS_HID] = new LogContainer("IOS_HID", "IOS - USB_HID");
m_Log[LogTypes::IOS_SD] = new LogContainer("IOS_SD", "IOS - SDIO");
m_Log[LogTypes::IOS_SSL] = new LogContainer("IOS_SSL", "IOS - SSL");
m_Log[LogTypes::IOS_STM] = new LogContainer("IOS_STM", "IOS - State Transition Manager");
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 44bae6b2c6..594608b377 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -157,7 +157,9 @@ set(SRCS ActionReplay.cpp
IOS/USB/Host.cpp
IOS/USB/OH0/OH0.cpp
IOS/USB/OH0/OH0Device.cpp
+ IOS/USB/USB_HID/HIDv4.cpp
IOS/USB/USBV0.cpp
+ IOS/USB/USBV4.cpp
IOS/USB/USB_KBD.cpp
IOS/USB/USB_VEN.cpp
IOS/USB/Bluetooth/BTBase.cpp
@@ -268,7 +270,6 @@ if(LIBUSB_FOUND)
# Using shared LibUSB
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
set(SRCS ${SRCS} IOS/USB/LibusbDevice.cpp
- IOS/USB/USB_HIDv4.cpp
IOS/USB/Bluetooth/BTReal.cpp)
endif()
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index 912c038179..7983948be3 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -194,14 +194,9 @@
+
-
-
- 4200;%(DisableSpecificWarnings)
-
+
@@ -431,8 +426,9 @@
+
-
+
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 0c7ba536db..3b9ad3503a 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -785,10 +785,13 @@
IOS\USB
+
+ IOS\USB
+
IOS\USB
-
+
IOS\USB
@@ -1377,10 +1380,13 @@
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 16443efa7e..0db81b2efc 100644
--- a/Source/Core/Core/IOS/IPC.cpp
+++ b/Source/Core/Core/IOS/IPC.cpp
@@ -51,6 +51,7 @@
#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_HID/HIDv4.h"
#include "Core/IOS/USB/USB_KBD.h"
#include "Core/IOS/USB/USB_VEN.h"
#include "Core/IOS/WFS/WFSI.h"
@@ -61,10 +62,6 @@ namespace CoreTiming
struct EventType;
} // namespace CoreTiming
-#if defined(__LIBUSB__)
-#include "Core/IOS/USB/USB_HIDv4.h"
-#endif
-
namespace IOS
{
namespace HLE
@@ -513,11 +510,7 @@ void Reinit()
AddDevice("/dev/usb/ven");
AddDevice("/dev/sdio/slot0");
AddDevice("/dev/sdio/slot1");
-#if defined(__LIBUSB__)
AddDevice("/dev/usb/hid");
-#else
- AddDevice("/dev/usb/hid");
-#endif
AddDevice("/dev/usb/oh0");
AddDevice("/dev/usb/oh1");
AddDevice("/dev/usb/wfssrv");
diff --git a/Source/Core/Core/IOS/USB/USBV4.cpp b/Source/Core/Core/IOS/USB/USBV4.cpp
new file mode 100644
index 0000000000..fae4e2011a
--- /dev/null
+++ b/Source/Core/Core/IOS/USB/USBV4.cpp
@@ -0,0 +1,97 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+#include
+#include
+
+#include "Common/CommonFuncs.h"
+#include "Common/CommonTypes.h"
+#include "Core/HW/Memmap.h"
+#include "Core/IOS/Device.h"
+#include "Core/IOS/USB/USBV4.h"
+
+namespace IOS
+{
+namespace HLE
+{
+namespace USB
+{
+// Source: https://wiibrew.org/w/index.php?title=/dev/usb/hid&oldid=96809
+#pragma pack(push, 1)
+struct HIDRequest
+{
+ u8 padding[16];
+ s32 device_no;
+ union
+ {
+ struct
+ {
+ u8 bmRequestType;
+ u8 bmRequest;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+ } control;
+ struct
+ {
+ u32 endpoint;
+ u32 length;
+ } interrupt;
+ struct
+ {
+ u8 bIndex;
+ } string;
+ };
+ u32 data_addr;
+};
+#pragma pack(pop)
+
+V4CtrlMessage::V4CtrlMessage(const IOCtlRequest& ioctl) : CtrlMessage(ioctl, -1)
+{
+ HIDRequest hid_request;
+ Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request));
+ request_type = hid_request.control.bmRequestType;
+ request = hid_request.control.bmRequest;
+ value = Common::swap16(hid_request.control.wValue);
+ index = Common::swap16(hid_request.control.wIndex);
+ length = Common::swap16(hid_request.control.wLength);
+ data_address = Common::swap32(hid_request.data_addr);
+}
+
+// Since this is just a standard control request, but with additional requirements
+// (US for the language and replacing non-ASCII characters with '?'),
+// we can simply submit it as a usual control request.
+V4GetUSStringMessage::V4GetUSStringMessage(const IOCtlRequest& ioctl) : CtrlMessage(ioctl, -1)
+{
+ HIDRequest hid_request;
+ Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request));
+ request_type = 0x80;
+ request = REQUEST_GET_DESCRIPTOR;
+ value = (0x03 << 8) | hid_request.string.bIndex;
+ index = 0x0409; // language US
+ length = 255;
+ data_address = Common::swap32(hid_request.data_addr);
+}
+
+void V4GetUSStringMessage::OnTransferComplete() const
+{
+ const std::locale& c_locale = std::locale::classic();
+ std::string message = Memory::GetString(data_address);
+ std::replace_if(message.begin(), message.end(),
+ [&c_locale](char c) { return !std::isprint(c, c_locale); }, '?');
+ Memory::CopyToEmu(data_address, message.c_str(), message.size());
+}
+
+V4IntrMessage::V4IntrMessage(const IOCtlRequest& ioctl) : IntrMessage(ioctl, -1)
+{
+ HIDRequest hid_request;
+ Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request));
+ length = static_cast(Common::swap32(hid_request.interrupt.length));
+ endpoint = static_cast(Common::swap32(hid_request.interrupt.endpoint));
+ data_address = Common::swap32(hid_request.data_addr);
+}
+} // namespace USB
+} // namespace HLE
+} // namespace IOS
diff --git a/Source/Core/Core/IOS/USB/USBV4.h b/Source/Core/Core/IOS/USB/USBV4.h
new file mode 100644
index 0000000000..93ccd7ee21
--- /dev/null
+++ b/Source/Core/Core/IOS/USB/USBV4.h
@@ -0,0 +1,50 @@
+// 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 an early version of /dev/usb/hid.
+
+namespace IOS
+{
+namespace HLE
+{
+struct IOCtlRequest;
+
+namespace USB
+{
+enum V4Requests
+{
+ IOCTL_USBV4_GETDEVICECHANGE = 0,
+ IOCTL_USBV4_SET_SUSPEND = 1,
+ IOCTL_USBV4_CTRLMSG = 2,
+ IOCTL_USBV4_INTRMSG_IN = 3,
+ IOCTL_USBV4_INTRMSG_OUT = 4,
+ IOCTL_USBV4_GET_US_STRING = 5,
+ IOCTL_USBV4_GETVERSION = 6,
+ IOCTL_USBV4_SHUTDOWN = 7,
+ IOCTL_USBV4_CANCELINTERRUPT = 8,
+};
+
+struct V4CtrlMessage final : CtrlMessage
+{
+ explicit V4CtrlMessage(const IOCtlRequest& ioctl);
+};
+
+struct V4GetUSStringMessage final : CtrlMessage
+{
+ explicit V4GetUSStringMessage(const IOCtlRequest& ioctl);
+ void OnTransferComplete() const override;
+};
+
+struct V4IntrMessage final : IntrMessage
+{
+ explicit V4IntrMessage(const IOCtlRequest& ioctl);
+};
+} // namespace USB
+} // namespace HLE
+} // namespace IOS
diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp
new file mode 100644
index 0000000000..4734490f1c
--- /dev/null
+++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.cpp
@@ -0,0 +1,230 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+#include
+
+#include "Common/Align.h"
+#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/USBV4.h"
+#include "Core/IOS/USB/USB_HID/HIDv4.h"
+
+namespace IOS
+{
+namespace HLE
+{
+namespace Device
+{
+USB_HIDv4::USB_HIDv4(u32 device_id, const std::string& device_name)
+ : USBHost(device_id, device_name)
+{
+}
+
+USB_HIDv4::~USB_HIDv4()
+{
+ StopThreads();
+}
+
+IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request)
+{
+ request.Log(GetDeviceName(), LogTypes::IOS_USB);
+ switch (request.request)
+ {
+ case USB::IOCTL_USBV4_GETVERSION:
+ return GetDefaultReply(VERSION);
+ case USB::IOCTL_USBV4_GETDEVICECHANGE:
+ return GetDeviceChange(request);
+ case USB::IOCTL_USBV4_SHUTDOWN:
+ return Shutdown(request);
+ case USB::IOCTL_USBV4_SET_SUSPEND:
+ // Not implemented in IOS
+ return GetDefaultReply(IPC_SUCCESS);
+ case USB::IOCTL_USBV4_CANCELINTERRUPT:
+ return CancelInterrupt(request);
+ case USB::IOCTL_USBV4_GET_US_STRING:
+ case USB::IOCTL_USBV4_CTRLMSG:
+ case USB::IOCTL_USBV4_INTRMSG_IN:
+ case USB::IOCTL_USBV4_INTRMSG_OUT:
+ {
+ if (request.buffer_in == 0 || request.buffer_in_size != 32)
+ return GetDefaultReply(IPC_EINVAL);
+ const auto device = GetDeviceByIOSID(Memory::Read_U32(request.buffer_in + 16));
+ if (!device->Attach(0))
+ return GetDefaultReply(IPC_EINVAL);
+ return HandleTransfer(device, request.request,
+ [&, this]() { return SubmitTransfer(*device, request); });
+ }
+ default:
+ request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB);
+ return GetDefaultReply(IPC_SUCCESS);
+ }
+}
+
+IPCCommandResult USB_HIDv4::CancelInterrupt(const IOCtlRequest& request)
+{
+ if (request.buffer_in == 0 || request.buffer_in_size != 8)
+ return GetDefaultReply(IPC_EINVAL);
+
+ auto device = GetDeviceByIOSID(Memory::Read_U32(request.buffer_in));
+ if (!device)
+ return GetDefaultReply(IPC_ENOENT);
+ device->CancelTransfer(Memory::Read_U8(request.buffer_in + 4));
+ return GetDefaultReply(IPC_SUCCESS);
+}
+
+IPCCommandResult USB_HIDv4::GetDeviceChange(const IOCtlRequest& request)
+{
+ std::lock_guard lk{m_devicechange_hook_address_mutex};
+ if (request.buffer_out == 0 || request.buffer_out_size != 0x600)
+ return GetDefaultReply(IPC_EINVAL);
+
+ 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_HIDv4::Shutdown(const IOCtlRequest& request)
+{
+ std::lock_guard lk{m_devicechange_hook_address_mutex};
+ if (m_devicechange_hook_request != 0)
+ {
+ Memory::Write_U32(0xffffffff, m_devicechange_hook_request->buffer_out);
+ EnqueueReply(*m_devicechange_hook_request, -1);
+ m_devicechange_hook_request.reset();
+ }
+ return GetDefaultReply(IPC_SUCCESS);
+}
+
+s32 USB_HIDv4::SubmitTransfer(USB::Device& device, const IOCtlRequest& request)
+{
+ switch (request.request)
+ {
+ case USB::IOCTL_USBV4_CTRLMSG:
+ return device.SubmitTransfer(std::make_unique(request));
+ case USB::IOCTL_USBV4_GET_US_STRING:
+ return device.SubmitTransfer(std::make_unique(request));
+ case USB::IOCTL_USBV4_INTRMSG_IN:
+ case USB::IOCTL_USBV4_INTRMSG_OUT:
+ return device.SubmitTransfer(std::make_unique(request));
+ default:
+ return IPC_EINVAL;
+ }
+}
+
+void USB_HIDv4::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_ios_ids);
+ p.Do(m_device_ids);
+
+ USBHost::DoState(p);
+}
+
+std::shared_ptr USB_HIDv4::GetDeviceByIOSID(const s32 ios_id) const
+{
+ std::lock_guard lk{m_id_map_mutex};
+ const auto iterator = m_ios_ids.find(ios_id);
+ if (iterator == m_ios_ids.cend())
+ return nullptr;
+ return GetDeviceById(iterator->second);
+}
+
+void USB_HIDv4::OnDeviceChange(ChangeEvent event, std::shared_ptr device)
+{
+ {
+ std::lock_guard id_map_lock{m_id_map_mutex};
+ if (event == ChangeEvent::Inserted)
+ {
+ s32 new_id = 0;
+ while (m_ios_ids.find(new_id) != m_ios_ids.cend())
+ ++new_id;
+ m_ios_ids[new_id] = device->GetId();
+ m_device_ids[device->GetId()] = new_id;
+ }
+ else if (event == ChangeEvent::Removed &&
+ m_device_ids.find(device->GetId()) != m_device_ids.cend())
+ {
+ m_ios_ids.erase(m_device_ids.at(device->GetId()));
+ m_device_ids.erase(device->GetId());
+ }
+ }
+
+ {
+ std::lock_guard lk{m_devicechange_hook_address_mutex};
+ TriggerDeviceChangeReply();
+ }
+}
+
+bool USB_HIDv4::ShouldAddDevice(const USB::Device& device) const
+{
+ return device.HasClass(HID_CLASS);
+}
+
+void USB_HIDv4::TriggerDeviceChangeReply()
+{
+ if (!m_devicechange_hook_request)
+ return;
+
+ {
+ std::lock_guard lk(m_devices_mutex);
+ const u32 dest = m_devicechange_hook_request->buffer_out;
+ u32 offset = 0;
+ for (const auto& device : m_devices)
+ {
+ const std::vector device_section = GetDeviceEntry(*device.second.get());
+ if (offset + device_section.size() > m_devicechange_hook_request->buffer_out_size - 1)
+ {
+ WARN_LOG(IOS_USB, "Too many devices connected, skipping");
+ break;
+ }
+ Memory::CopyToEmu(dest + offset, device_section.data(), device_section.size());
+ offset += Common::AlignUp(static_cast(device_section.size()), 4);
+ }
+ // IOS writes 0xffffffff to the buffer when there are no more devices
+ Memory::Write_U32(0xffffffff, dest + offset);
+ }
+
+ EnqueueReply(*m_devicechange_hook_request, IPC_SUCCESS, 0, CoreTiming::FromThread::ANY);
+ m_devicechange_hook_request.reset();
+}
+
+std::vector USB_HIDv4::GetDeviceEntry(const USB::Device& device) const
+{
+ std::lock_guard id_map_lock{m_id_map_mutex};
+
+ // The structure for a device section is as follows:
+ // 0-4 bytes: total size of the device data, including the size and the device ID
+ // 4-8 bytes: device ID
+ // the rest of the buffer is device descriptors data
+ std::vector entry(8);
+ const std::vector descriptors = device.GetDescriptorsUSBV4();
+ const u32 entry_size = Common::swap32(static_cast(entry.size() + descriptors.size()));
+ const u32 ios_device_id = Common::swap32(m_device_ids.at(device.GetId()));
+ std::memcpy(entry.data(), &entry_size, sizeof(entry_size));
+ std::memcpy(entry.data() + 4, &ios_device_id, sizeof(ios_device_id));
+ entry.insert(entry.end(), descriptors.begin(), descriptors.end());
+
+ return entry;
+}
+} // namespace Device
+} // namespace HLE
+} // namespace IOS
diff --git a/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h
new file mode 100644
index 0000000000..4048f3c339
--- /dev/null
+++ b/Source/Core/Core/IOS/USB/USB_HID/HIDv4.h
@@ -0,0 +1,62 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include