Skip to content
Permalink
Browse files

Merge pull request #8109 from leoetlino/ios-usb-fixes

IOS: USB fixes
  • Loading branch information...
leoetlino committed May 24, 2019
2 parents e07b514 + 6dd0fe2 commit 5fb56505b22f5050f6ae0d59c46b4b1450b0fc3d
@@ -15,6 +15,8 @@

namespace IOS::HLE::USB
{
constexpr u8 DEFAULT_CONFIG_NUM = 0;

enum StandardDeviceRequestCodes
{
REQUEST_GET_DESCRIPTOR = 6,
@@ -127,14 +129,14 @@ struct CtrlMessage : TransferCommand

struct BulkMessage : TransferCommand
{
u16 length = 0;
u32 length = 0;
u8 endpoint = 0;
using TransferCommand::TransferCommand;
};

struct IntrMessage : TransferCommand
{
u16 length = 0;
u32 length = 0;
u8 endpoint = 0;
using TransferCommand::TransferCommand;
};
@@ -143,7 +145,7 @@ struct IsoMessage : TransferCommand
{
u32 packet_sizes_addr = 0;
std::vector<u16> packet_sizes;
u16 length = 0;
u32 length = 0;
u8 num_packets = 0;
u8 endpoint = 0;
using TransferCommand::TransferCommand;
@@ -165,7 +167,13 @@ class Device
virtual std::vector<EndpointDescriptor> GetEndpoints(u8 config, u8 interface, u8 alt) const = 0;

virtual std::string GetErrorName(int error_code) const;
virtual bool Attach(u8 interface) = 0;
/// Ensure the device is ready to use.
virtual bool Attach() = 0;
/// Ensure the device is ready to use and change the active interface (if needed).
///
/// This may reset the active alt setting, so prefer using Attach when interface changes
/// are unnecessary (e.g. for control requests).
virtual bool AttachAndChangeInterface(u8 interface) = 0;
virtual int CancelTransfer(u8 endpoint) = 0;
virtual int ChangeInterface(u8 interface) = 0;
virtual int GetNumberOfAltSettings(u8 interface) = 0;
@@ -45,12 +45,14 @@ USBHost::~USBHost()

IPCCommandResult USBHost::Open(const OpenRequest& request)
{
// Force a device scan to complete, because some games (including Your Shape) only care
// about the initial device list (in the first GETDEVICECHANGE reply).
while (!UpdateDevices())
if (!m_has_initialised)
{
StartThreads();
// Force a device scan to complete, because some games (including Your Shape) only care
// about the initial device list (in the first GETDEVICECHANGE reply).
m_first_scan_complete_event.Wait();
m_has_initialised = true;
}
StartThreads();
return GetDefaultReply(IPC_SUCCESS);
}

@@ -117,6 +119,7 @@ bool USBHost::UpdateDevices(const bool always_add_hooks)
return false;
DetectRemovedDevices(plugged_devices, hooks);
DispatchHooks(hooks);
m_first_scan_complete_event.Set();
return true;
}

@@ -15,6 +15,7 @@
#include <vector>

#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/IOS.h"
@@ -76,5 +77,7 @@ class USBHost : public Device
// Device scanning thread
Common::Flag m_scan_thread_running;
std::thread m_scan_thread;
Common::Event m_first_scan_complete_event;
bool m_has_initialised = false;
};
} // namespace IOS::HLE::Device
@@ -18,6 +18,7 @@

#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/IOS.h"
@@ -41,10 +42,11 @@ LibusbDevice::LibusbDevice(Kernel& ios, libusb_device* device,

LibusbDevice::~LibusbDevice()
{
if (m_device_attached)
DetachInterface();
if (m_handle != nullptr)
{
ReleaseAllInterfacesForCurrentConfig();
libusb_close(m_handle);
}
libusb_unref_device(m_device);
}

@@ -123,28 +125,39 @@ std::string LibusbDevice::GetErrorName(const int error_code) const
return libusb_error_name(error_code);
}

bool LibusbDevice::Attach(const u8 interface)
bool LibusbDevice::Attach()
{
if (m_device_attached && interface != m_active_interface)
return ChangeInterface(interface) == 0;

if (m_device_attached)
return true;

m_device_attached = false;
NOTICE_LOG(IOS_USB, "[%04x:%04x] Opening device", m_vid, m_pid);
const int ret = libusb_open(m_device, &m_handle);
if (ret != 0)
if (!m_handle)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to open: %s", m_vid, m_pid, libusb_error_name(ret));
return false;
NOTICE_LOG(IOS_USB, "[%04x:%04x] Opening device", m_vid, m_pid);
const int ret = libusb_open(m_device, &m_handle);
if (ret != 0)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to open: %s", m_vid, m_pid, libusb_error_name(ret));
m_handle = nullptr;
return false;
}
}
if (AttachInterface(interface) != 0)
if (ClaimAllInterfaces(DEFAULT_CONFIG_NUM) < 0)
return false;
m_device_attached = true;
return true;
}

bool LibusbDevice::AttachAndChangeInterface(const u8 interface)
{
if (!Attach())
return false;

if (interface != m_active_interface)
return ChangeInterface(interface) == 0;

return true;
}

int LibusbDevice::CancelTransfer(const u8 endpoint)
{
INFO_LOG(IOS_USB, "[%04x:%04x %d] Cancelling transfers (endpoint 0x%x)", m_vid, m_pid,
@@ -158,15 +171,10 @@ int LibusbDevice::CancelTransfer(const u8 endpoint)

int LibusbDevice::ChangeInterface(const u8 interface)
{
if (!m_device_attached || interface >= m_config_descriptors[0]->Get()->bNumInterfaces)
return LIBUSB_ERROR_NOT_FOUND;

INFO_LOG(IOS_USB, "[%04x:%04x %d] Changing interface to %d", m_vid, m_pid, m_active_interface,
interface);
const int ret = DetachInterface();
if (ret < 0)
return ret;
return AttachInterface(interface);
m_active_interface = interface;
return 0;
}

int LibusbDevice::SetAltSetting(const u8 alt_setting)
@@ -184,11 +192,19 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr<CtrlMessage> cmd)
if (!m_device_attached)
return LIBUSB_ERROR_NOT_FOUND;

DEBUG_LOG(IOS_USB,
"[%04x:%04x %d] Control: bRequestType=%02x bRequest=%02x wValue=%04x"
" wIndex=%04x wLength=%04x",
m_vid, m_pid, m_active_interface, cmd->request_type, cmd->request, cmd->value,
cmd->index, cmd->length);

switch ((cmd->request_type << 8) | cmd->request)
{
// The following requests have to go through libusb and cannot be directly sent to the device.
case USBHDR(DIR_HOST2DEVICE, TYPE_STANDARD, REC_INTERFACE, REQUEST_SET_INTERFACE):
{
INFO_LOG(IOS_USB, "[%04x:%04x %d] REQUEST_SET_INTERFACE index=%04x value=%04x", m_vid, m_pid,
m_active_interface, cmd->index, cmd->value);
if (static_cast<u8>(cmd->index) != m_active_interface)
{
const int ret = ChangeInterface(static_cast<u8>(cmd->index));
@@ -206,9 +222,15 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr<CtrlMessage> cmd)
}
case USBHDR(DIR_HOST2DEVICE, TYPE_STANDARD, REC_DEVICE, REQUEST_SET_CONFIGURATION):
{
INFO_LOG(IOS_USB, "[%04x:%04x %d] REQUEST_SET_CONFIGURATION index=%04x value=%04x", m_vid,
m_pid, m_active_interface, cmd->index, cmd->value);
ReleaseAllInterfacesForCurrentConfig();
const int ret = libusb_set_configuration(m_handle, cmd->value);
if (ret == 0)
{
ClaimAllInterfaces(cmd->value);
m_ios.EnqueueIPCReply(cmd->ios_request, cmd->length);
}
return ret;
}
}
@@ -230,6 +252,9 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr<BulkMessage> cmd)
if (!m_device_attached)
return LIBUSB_ERROR_NOT_FOUND;

DEBUG_LOG(IOS_USB, "[%04x:%04x %d] Bulk: length=%04x endpoint=%02x", m_vid, m_pid,
m_active_interface, cmd->length, cmd->endpoint);

libusb_transfer* transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, m_handle, cmd->endpoint,
cmd->MakeBuffer(cmd->length).release(), cmd->length, TransferCallback,
@@ -244,6 +269,9 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr<IntrMessage> cmd)
if (!m_device_attached)
return LIBUSB_ERROR_NOT_FOUND;

DEBUG_LOG(IOS_USB, "[%04x:%04x %d] Interrupt: length=%04x endpoint=%02x", m_vid, m_pid,
m_active_interface, cmd->length, cmd->endpoint);

libusb_transfer* transfer = libusb_alloc_transfer(0);
libusb_fill_interrupt_transfer(transfer, m_handle, cmd->endpoint,
cmd->MakeBuffer(cmd->length).release(), cmd->length,
@@ -258,6 +286,9 @@ int LibusbDevice::SubmitTransfer(std::unique_ptr<IsoMessage> cmd)
if (!m_device_attached)
return LIBUSB_ERROR_NOT_FOUND;

DEBUG_LOG(IOS_USB, "[%04x:%04x %d] Isochronous: length=%04x endpoint=%02x num_packets=%02x",
m_vid, m_pid, m_active_interface, cmd->length, cmd->endpoint, cmd->num_packets);

libusb_transfer* transfer = libusb_alloc_transfer(cmd->num_packets);
transfer->buffer = cmd->MakeBuffer(cmd->length).release();
transfer->callback = TransferCallback;
@@ -375,50 +406,61 @@ int LibusbDevice::GetNumberOfAltSettings(const u8 interface_number)
return m_config_descriptors[0]->Get()->interface[interface_number].num_altsetting;
}

int LibusbDevice::AttachInterface(const u8 interface)
template <typename Configs, typename Function>
static int DoForEachInterface(const Configs& configs, u8 config_num, Function action)
{
if (m_handle == nullptr)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Cannot attach without a valid device handle", m_vid, m_pid);
return -1;
}

INFO_LOG(IOS_USB, "[%04x:%04x] Attaching interface %d", m_vid, m_pid, interface);
const int ret = libusb_detach_kernel_driver(m_handle, interface);
if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND && ret != LIBUSB_ERROR_NOT_SUPPORTED)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to detach kernel driver: %s", m_vid, m_pid,
libusb_error_name(ret));
int ret = LIBUSB_ERROR_NOT_FOUND;
if (configs.size() <= config_num || !configs[config_num]->IsValid())
return ret;
}
const int r = libusb_claim_interface(m_handle, interface);
if (r < 0)
for (u8 i = 0; i < configs[config_num]->Get()->bNumInterfaces; ++i)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Couldn't claim interface %d: %s", m_vid, m_pid, interface,
libusb_error_name(r));
return r;
ret = action(i);
if (ret < 0)
break;
}
m_active_interface = interface;
return 0;
return ret;
}

int LibusbDevice::DetachInterface()
int LibusbDevice::ClaimAllInterfaces(u8 config_num) const
{
if (m_handle == nullptr)
const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](u8 i) {
const int ret2 = libusb_detach_kernel_driver(m_handle, i);
if (ret2 < 0 && ret2 != LIBUSB_ERROR_NOT_FOUND && ret2 != LIBUSB_ERROR_NOT_SUPPORTED)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to detach kernel driver: %s", m_vid, m_pid,
libusb_error_name(ret2));
return ret2;
}
return libusb_claim_interface(m_handle, i);
});
if (ret < 0)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Cannot detach without a valid device handle", m_vid, m_pid);
return -1;
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to claim all interfaces (configuration %u)", m_vid,
m_pid, config_num);
}
return ret;
}

INFO_LOG(IOS_USB, "[%04x:%04x] Detaching interface %d", m_vid, m_pid, m_active_interface);
const int ret = libusb_release_interface(m_handle, m_active_interface);
if (ret < 0 && ret != LIBUSB_ERROR_NO_DEVICE)
int LibusbDevice::ReleaseAllInterfaces(u8 config_num) const
{
const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](u8 i) {
return libusb_release_interface(m_handle, i);
});
if (ret < 0 && ret != LIBUSB_ERROR_NO_DEVICE && ret != LIBUSB_ERROR_NOT_FOUND)
{
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to release interface %d: %s", m_vid, m_pid,
m_active_interface, libusb_error_name(ret));
return ret;
ERROR_LOG(IOS_USB, "[%04x:%04x] Failed to release all interfaces (configuration %u)", m_vid,
m_pid, config_num);
}
return 0;
return ret;
}

int LibusbDevice::ReleaseAllInterfacesForCurrentConfig() const
{
int config_num;
const int get_config_ret = libusb_get_configuration(m_handle, &config_num);
if (get_config_ret < 0)
return get_config_ret;
return ReleaseAllInterfaces(config_num);
}

LibusbConfigDescriptor::LibusbConfigDescriptor(libusb_device* device, const u8 config_num)
@@ -48,7 +48,8 @@ class LibusbDevice final : public Device
std::vector<InterfaceDescriptor> GetInterfaces(u8 config) const override;
std::vector<EndpointDescriptor> GetEndpoints(u8 config, u8 interface, u8 alt) const override;
std::string GetErrorName(int error_code) const override;
bool Attach(u8 interface) override;
bool Attach() override;
bool AttachAndChangeInterface(u8 interface) override;
int CancelTransfer(u8 endpoint) override;
int ChangeInterface(u8 interface) override;
int GetNumberOfAltSettings(u8 interface) override;
@@ -85,8 +86,9 @@ class LibusbDevice final : public Device
static void CtrlTransferCallback(libusb_transfer* transfer);
static void TransferCallback(libusb_transfer* transfer);

int AttachInterface(u8 interface);
int DetachInterface();
int ClaimAllInterfaces(u8 config_num) const;
int ReleaseAllInterfaces(u8 config_num) const;
int ReleaseAllInterfacesForCurrentConfig() const;
};
} // namespace IOS::HLE::USB
#endif
@@ -252,8 +252,10 @@ std::pair<ReturnCode, u64> OH0::DeviceOpen(const u16 vid, const u16 pid)
has_device_with_vid_pid = true;

if (m_opened_devices.find(device.second->GetId()) != m_opened_devices.cend() ||
!device.second->Attach(0))
!device.second->Attach())
{
continue;
}

m_opened_devices.emplace(device.second->GetId());
return {IPC_SUCCESS, device.second->GetId()};
@@ -87,7 +87,7 @@ V4IntrMessage::V4IntrMessage(Kernel& ios, const IOCtlRequest& ioctl) : IntrMessa
{
HIDRequest hid_request;
Memory::CopyFromEmu(&hid_request, ioctl.buffer_in, sizeof(hid_request));
length = static_cast<u16>(Common::swap32(hid_request.interrupt.length));
length = Common::swap32(hid_request.interrupt.length);
endpoint = static_cast<u8>(Common::swap32(hid_request.interrupt.endpoint));
data_address = Common::swap32(hid_request.data_addr);
}

0 comments on commit 5fb5650

Please sign in to comment.
You can’t perform that action at this time.