AnyPS5/core/libs/prx/libSceUsbd/Export.cpp

538 lines
24 KiB
C++

#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include <libusb.h>
#include <chrono>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <new>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>
#include <unordered_set>
namespace {
constexpr std::int32_t SCE_USBD_ERROR_INVALID_ARG = static_cast<std::int32_t>(0x80240002);
constexpr std::int32_t SCE_USBD_ERROR_NOT_FOUND = static_cast<std::int32_t>(0x80240005);
constexpr std::int32_t SCE_USBD_ERROR_OTHER = static_cast<std::int32_t>(0x802400FF);
constexpr std::uint8_t TRANSFER_TYPE_CONTROL = 0;
constexpr std::uint8_t TRANSFER_TYPE_ISOCHRONOUS = 1;
constexpr std::uint8_t TRANSFER_TYPE_BULK = 2;
constexpr std::uint8_t TRANSFER_TYPE_INTERRUPT = 3;
constexpr std::int32_t CONTROL_SETUP_SIZE = 8;
constexpr std::uint8_t SUPPORTED_TRANSFER_FLAGS = LIBUSB_TRANSFER_SHORT_NOT_OK | LIBUSB_TRANSFER_ADD_ZERO_PACKET;
struct UsbdTimeval {
std::int64_t seconds;
std::int64_t microseconds;
};
struct UsbdIsoPacketDescriptor {
std::uint32_t length;
std::uint32_t actualLength;
std::int32_t status;
};
using UsbdTransferCallback = void (APS5_VABI*)(struct UsbdTransfer*);
struct UsbdTransfer {
void* deviceHandle;
std::uint8_t flags;
std::uint8_t endpoint;
std::uint8_t type;
std::uint32_t timeout;
std::int32_t status;
std::int32_t length;
std::int32_t actualLength;
UsbdTransferCallback callback;
void* userData;
std::uint8_t* buffer;
std::int32_t numIsoPackets;
UsbdIsoPacketDescriptor isoPacketDescriptors[1];
};
static_assert(offsetof(UsbdTransfer, timeout) == 12 && offsetof(UsbdTransfer, callback) == 32 && offsetof(UsbdTransfer, numIsoPackets) == 56 && offsetof(UsbdTransfer, isoPacketDescriptors) == 60);
static_assert(sizeof(libusb_device_descriptor) == 18);
static_assert(sizeof(libusb_config_descriptor) == 40 && offsetof(libusb_config_descriptor, interface) == 16 && offsetof(libusb_config_descriptor, extra) == 24 && offsetof(libusb_config_descriptor, extra_length) == 32);
static_assert(sizeof(libusb_interface) == 16 && offsetof(libusb_interface, num_altsetting) == 8);
static_assert(sizeof(libusb_interface_descriptor) == 40 && offsetof(libusb_interface_descriptor, endpoint) == 16 && offsetof(libusb_interface_descriptor, extra) == 24 && offsetof(libusb_interface_descriptor, extra_length) == 32);
static_assert(sizeof(libusb_endpoint_descriptor) == 32 && offsetof(libusb_endpoint_descriptor, wMaxPacketSize) == 4 && offsetof(libusb_endpoint_descriptor, extra) == 16 && offsetof(libusb_endpoint_descriptor, extra_length) == 24);
static_assert(sizeof(libusb_iso_packet_descriptor) == sizeof(UsbdIsoPacketDescriptor));
struct UsbdState {
std::mutex mutex;
libusb_context* context = nullptr;
std::uint32_t initCount = 0;
std::unordered_map<libusb_device*, std::uint32_t> devices;
std::unordered_set<libusb_device**> lists;
std::unordered_set<libusb_device_handle*> handles;
std::unordered_set<libusb_config_descriptor*> configs;
std::unordered_map<UsbdTransfer*, libusb_transfer*> inFlight;
};
UsbdState& State() {
static auto* state = new UsbdState();
return *state;
}
void* g_emptyDeviceList[1] = {nullptr};
std::string AbsentDevice(const char* function) {
return std::string(function) + ": the device was not returned by sceUsbdGetDeviceList or was already released";
}
std::string UnopenedHandle(const char* function) {
return std::string(function) + ": the handle was not returned by sceUsbdOpen or was already closed";
}
std::int32_t ToGuestError(int result) {
if (result >= 0) return result;
if (result == LIBUSB_ERROR_OTHER) return SCE_USBD_ERROR_OTHER;
return static_cast<std::int32_t>(0x80240000u - static_cast<std::uint32_t>(result));
}
libusb_device* KnownDevice(void* device, const char* function) {
auto& state = State();
std::lock_guard lock(state.mutex);
auto* host = static_cast<libusb_device*>(device);
if (!state.devices.contains(host)) throw std::runtime_error(AbsentDevice(function));
return host;
}
libusb_device_handle* KnownHandle(void* deviceHandle, const char* function) {
auto& state = State();
std::lock_guard lock(state.mutex);
auto* host = static_cast<libusb_device_handle*>(deviceHandle);
if (!state.handles.contains(host)) throw std::runtime_error(UnopenedHandle(function));
return host;
}
void AddDeviceReference(UsbdState& state, libusb_device* device) {
++state.devices[device];
}
void DropDeviceReference(UsbdState& state, libusb_device* device) {
const auto found = state.devices.find(device);
if (found == state.devices.end()) throw std::runtime_error(AbsentDevice("sceUsbdUnrefDevice"));
if (--found->second == 0) state.devices.erase(found);
}
void LIBUSB_CALL CompleteTransfer(libusb_transfer* host) {
auto* guest = static_cast<UsbdTransfer*>(host->user_data);
guest->status = host->status;
guest->actualLength = host->actual_length;
for (int packet = 0; packet < host->num_iso_packets; ++packet) {
guest->isoPacketDescriptors[packet].actualLength = host->iso_packet_desc[packet].actual_length;
guest->isoPacketDescriptors[packet].status = host->iso_packet_desc[packet].status;
}
{
auto& state = State();
std::lock_guard lock(state.mutex);
state.inFlight.erase(guest);
}
libusb_free_transfer(host);
if (guest->callback != nullptr) guest->callback(guest);
}
}
extern "C" {
std::int32_t APS5_VABI sceUsbdInit() {
auto& state = State();
std::lock_guard lock(state.mutex);
if (state.initCount == 0) {
libusb_context* context = nullptr;
if (libusb_init_context(&context, nullptr, 0) == 0) state.context = context;
}
++state.initCount;
return 0;
}
void APS5_VABI sceUsbdExit() {
auto& state = State();
std::lock_guard lock(state.mutex);
if (state.initCount == 0 || --state.initCount != 0) return;
if (!state.inFlight.empty()) throw std::runtime_error(std::string(__func__) + ": transfers are still in flight");
for (auto* handle : state.handles) libusb_close(handle);
for (auto* config : state.configs) libusb_free_config_descriptor(config);
for (auto* list : state.lists) libusb_free_device_list(list, 0);
for (const auto& [device, references] : state.devices) {
for (std::uint32_t reference = 0; reference < references; ++reference) libusb_unref_device(device);
}
state.handles.clear();
state.configs.clear();
state.lists.clear();
state.devices.clear();
if (state.context != nullptr) libusb_exit(state.context);
state.context = nullptr;
}
std::int64_t APS5_VABI sceUsbdGetDeviceList(void*** list) {
if (list == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
auto& state = State();
std::lock_guard lock(state.mutex);
if (state.context == nullptr) {
*list = g_emptyDeviceList;
return 0;
}
libusb_device** devices = nullptr;
const ssize_t count = libusb_get_device_list(state.context, &devices);
if (count < 0) return ToGuestError(static_cast<int>(count));
for (ssize_t index = 0; index < count; ++index) AddDeviceReference(state, devices[index]);
state.lists.insert(devices);
*list = reinterpret_cast<void**>(devices);
return count;
}
void APS5_VABI sceUsbdFreeDeviceList(void** list, std::int32_t unrefDevices) {
if (list == nullptr || list == g_emptyDeviceList) return;
auto& state = State();
std::lock_guard lock(state.mutex);
auto* devices = reinterpret_cast<libusb_device**>(list);
if (state.lists.erase(devices) == 0) throw std::runtime_error(std::string(__func__) + ": list was not returned by sceUsbdGetDeviceList");
if (unrefDevices != 0) {
for (auto** device = devices; *device != nullptr; ++device) DropDeviceReference(state, *device);
}
libusb_free_device_list(devices, unrefDevices);
}
std::int32_t APS5_VABI sceUsbdHandleEventsTimeout(const UsbdTimeval* timeout) {
if (timeout == nullptr || timeout->seconds < 0 || timeout->microseconds < 0 || timeout->microseconds >= 1000000) return SCE_USBD_ERROR_INVALID_ARG;
libusb_context* context;
{
auto& state = State();
std::lock_guard lock(state.mutex);
context = state.context;
}
if (context == nullptr) {
std::this_thread::sleep_for(std::chrono::seconds(timeout->seconds) + std::chrono::microseconds(timeout->microseconds));
return 0;
}
timeval host{};
host.tv_sec = static_cast<decltype(host.tv_sec)>(timeout->seconds);
host.tv_usec = static_cast<decltype(host.tv_usec)>(timeout->microseconds);
return ToGuestError(libusb_handle_events_timeout_completed(context, &host, nullptr));
}
UsbdTransfer* APS5_VABI sceUsbdAllocTransfer(std::int32_t isoPackets) {
if (isoPackets < 0) return nullptr;
const std::size_t size = offsetof(UsbdTransfer, isoPacketDescriptors) + sizeof(UsbdIsoPacketDescriptor) * static_cast<std::size_t>(isoPackets);
auto* transfer = static_cast<UsbdTransfer*>(std::calloc(1, size));
if (transfer == nullptr) return nullptr;
transfer->numIsoPackets = isoPackets;
return transfer;
}
void APS5_VABI sceUsbdFreeTransfer(UsbdTransfer* transfer) {
if (transfer == nullptr) return;
{
auto& state = State();
std::lock_guard lock(state.mutex);
if (state.inFlight.contains(transfer)) throw std::runtime_error(std::string(__func__) + ": transfer is still in flight");
}
std::free(transfer);
}
void APS5_VABI sceUsbdFillInterruptTransfer(UsbdTransfer* transfer, void* deviceHandle, std::uint8_t endpoint, std::uint8_t* buffer, std::int32_t length, UsbdTransferCallback callback, void* userData, std::uint32_t timeout) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
transfer->deviceHandle = deviceHandle;
transfer->endpoint = endpoint;
transfer->type = TRANSFER_TYPE_INTERRUPT;
transfer->timeout = timeout;
transfer->buffer = buffer;
transfer->length = length;
transfer->userData = userData;
transfer->callback = callback;
}
void APS5_VABI sceUsbdFillBulkTransfer(UsbdTransfer* transfer, void* deviceHandle, std::uint8_t endpoint, std::uint8_t* buffer, std::int32_t length, UsbdTransferCallback callback, void* userData, std::uint32_t timeout) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
transfer->deviceHandle = deviceHandle;
transfer->endpoint = endpoint;
transfer->type = TRANSFER_TYPE_BULK;
transfer->timeout = timeout;
transfer->buffer = buffer;
transfer->length = length;
transfer->userData = userData;
transfer->callback = callback;
}
void APS5_VABI sceUsbdFillIsoTransfer(UsbdTransfer* transfer, void* deviceHandle, std::uint8_t endpoint, std::uint8_t* buffer, std::int32_t length, std::int32_t numIsoPackets, UsbdTransferCallback callback, void* userData, std::uint32_t timeout) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
transfer->deviceHandle = deviceHandle;
transfer->endpoint = endpoint;
transfer->type = TRANSFER_TYPE_ISOCHRONOUS;
transfer->timeout = timeout;
transfer->buffer = buffer;
transfer->length = length;
transfer->numIsoPackets = numIsoPackets;
transfer->userData = userData;
transfer->callback = callback;
}
void APS5_VABI sceUsbdFillControlSetup(std::uint8_t* buffer, std::uint8_t requestType, std::uint8_t request, std::uint16_t value, std::uint16_t index, std::uint16_t length) {
if (buffer == nullptr) throw std::runtime_error(std::string(__func__) + ": null buffer");
buffer[0] = requestType;
buffer[1] = request;
buffer[2] = static_cast<std::uint8_t>(value);
buffer[3] = static_cast<std::uint8_t>(value >> 8);
buffer[4] = static_cast<std::uint8_t>(index);
buffer[5] = static_cast<std::uint8_t>(index >> 8);
buffer[6] = static_cast<std::uint8_t>(length);
buffer[7] = static_cast<std::uint8_t>(length >> 8);
}
void APS5_VABI sceUsbdFillControlTransfer(UsbdTransfer* transfer, void* deviceHandle, std::uint8_t* buffer, UsbdTransferCallback callback, void* userData, std::uint32_t timeout) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
transfer->deviceHandle = deviceHandle;
transfer->endpoint = 0;
transfer->type = TRANSFER_TYPE_CONTROL;
transfer->timeout = timeout;
transfer->buffer = buffer;
transfer->length = buffer != nullptr ? CONTROL_SETUP_SIZE + (buffer[6] | buffer[7] << 8) : 0;
transfer->userData = userData;
transfer->callback = callback;
}
std::uint8_t* APS5_VABI sceUsbdControlTransferGetData(UsbdTransfer* transfer) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
return transfer->buffer != nullptr ? transfer->buffer + CONTROL_SETUP_SIZE : nullptr;
}
std::uint8_t* APS5_VABI sceUsbdControlTransferGetSetup(UsbdTransfer* transfer) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
return transfer->buffer;
}
void APS5_VABI sceUsbdSetIsoPacketLengths(UsbdTransfer* transfer, std::uint32_t length) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
for (std::int32_t packet = 0; packet < transfer->numIsoPackets; ++packet) transfer->isoPacketDescriptors[packet].length = length;
}
std::uint8_t* APS5_VABI sceUsbdGetIsoPacketBuffer(UsbdTransfer* transfer, std::uint32_t packet) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
if (transfer->numIsoPackets < 0 || packet >= static_cast<std::uint32_t>(transfer->numIsoPackets) || transfer->buffer == nullptr) return nullptr;
std::uint8_t* pointer = transfer->buffer;
for (std::uint32_t index = 0; index < packet; ++index) pointer += transfer->isoPacketDescriptors[index].length;
return pointer;
}
std::int32_t APS5_VABI sceUsbdBulkTransfer(void* deviceHandle, std::uint8_t endpoint, std::uint8_t* data, std::int32_t length, std::int32_t* transferred, std::uint32_t timeout) {
if (deviceHandle == nullptr || (length != 0 && data == nullptr)) return SCE_USBD_ERROR_INVALID_ARG;
if (transferred == nullptr) throw std::runtime_error(std::string(__func__) + ": null transferred output");
*transferred = 0;
return ToGuestError(libusb_bulk_transfer(KnownHandle(deviceHandle, __func__), endpoint, data, length, transferred, timeout));
}
std::int32_t APS5_VABI sceUsbdInterruptTransfer(void* deviceHandle, std::uint8_t endpoint, std::uint8_t* data, std::int32_t length, std::int32_t* transferred, std::uint32_t timeout) {
if (deviceHandle == nullptr || (length != 0 && data == nullptr)) return SCE_USBD_ERROR_INVALID_ARG;
if (transferred == nullptr) throw std::runtime_error(std::string(__func__) + ": null transferred output");
*transferred = 0;
return ToGuestError(libusb_interrupt_transfer(KnownHandle(deviceHandle, __func__), endpoint, data, length, transferred, timeout));
}
std::int32_t APS5_VABI sceUsbdCancelTransfer(UsbdTransfer* transfer) {
if (transfer == nullptr) throw std::runtime_error(std::string(__func__) + ": null transfer");
auto& state = State();
std::lock_guard lock(state.mutex);
const auto found = state.inFlight.find(transfer);
if (found == state.inFlight.end()) return SCE_USBD_ERROR_NOT_FOUND;
return ToGuestError(libusb_cancel_transfer(found->second));
}
std::int32_t APS5_VABI sceUsbdEventHandlingOk() {
return 1;
}
void APS5_VABI sceUsbdFreeConfigDescriptor(void* config) {
if (config == nullptr) return;
auto& state = State();
std::lock_guard lock(state.mutex);
auto* host = static_cast<libusb_config_descriptor*>(config);
if (state.configs.erase(host) == 0) throw std::runtime_error(std::string(__func__) + ": descriptor was not returned by this library");
libusb_free_config_descriptor(host);
}
std::int32_t APS5_VABI sceUsbdAttachKernelDriver(void* deviceHandle, std::int32_t interfaceNumber) {
(void)interfaceNumber;
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
KnownHandle(deviceHandle, __func__);
return 0;
}
std::int32_t APS5_VABI sceUsbdCheckConnected(void* deviceHandle) {
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
int configuration = 0;
const int result = libusb_get_configuration(KnownHandle(deviceHandle, __func__), &configuration);
return result == LIBUSB_ERROR_NO_DEVICE ? ToGuestError(LIBUSB_ERROR_NO_DEVICE) : 0;
}
std::int32_t APS5_VABI sceUsbdClaimInterface(void* deviceHandle, std::int32_t interfaceNumber) {
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ToGuestError(libusb_claim_interface(KnownHandle(deviceHandle, __func__), interfaceNumber));
}
void APS5_VABI sceUsbdClose(void* deviceHandle) {
if (deviceHandle == nullptr) return;
auto& state = State();
std::lock_guard lock(state.mutex);
auto* host = static_cast<libusb_device_handle*>(deviceHandle);
if (state.handles.erase(host) == 0) throw std::runtime_error(UnopenedHandle(__func__));
libusb_close(host);
}
std::int32_t APS5_VABI sceUsbdControlTransfer(void* deviceHandle, std::uint8_t requestType, std::uint8_t request, std::uint16_t value, std::uint16_t index, std::uint8_t* data, std::uint16_t length, std::uint32_t timeout) {
if (deviceHandle == nullptr || (length != 0 && data == nullptr)) return SCE_USBD_ERROR_INVALID_ARG;
return ToGuestError(libusb_control_transfer(KnownHandle(deviceHandle, __func__), requestType, request, value, index, data, length, timeout));
}
std::int32_t APS5_VABI sceUsbdGetActiveConfigDescriptor(void* device, void** config) {
if (device == nullptr) throw std::runtime_error(std::string(__func__) + ": null device");
if (config == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
libusb_config_descriptor* host = nullptr;
const int result = libusb_get_active_config_descriptor(KnownDevice(device, __func__), &host);
*config = nullptr;
if (result != 0) return ToGuestError(result);
auto& state = State();
std::lock_guard lock(state.mutex);
state.configs.insert(host);
*config = host;
return 0;
}
std::uint8_t APS5_VABI sceUsbdGetBusNumber(void* device) {
if (device == nullptr) return 0;
return libusb_get_bus_number(KnownDevice(device, __func__));
}
std::int32_t APS5_VABI sceUsbdGetConfigDescriptor(void* device, std::uint8_t configIndex, void** config) {
if (device == nullptr || config == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
libusb_config_descriptor* host = nullptr;
const int result = libusb_get_config_descriptor(KnownDevice(device, __func__), configIndex, &host);
*config = nullptr;
if (result != 0) return ToGuestError(result);
auto& state = State();
std::lock_guard lock(state.mutex);
state.configs.insert(host);
*config = host;
return 0;
}
std::uint8_t APS5_VABI sceUsbdGetDeviceAddress(void* device) {
if (device == nullptr) return 0;
return libusb_get_device_address(KnownDevice(device, __func__));
}
std::int32_t APS5_VABI sceUsbdGetDeviceDescriptor(void* device, void* descriptor) {
if (device == nullptr || descriptor == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ToGuestError(libusb_get_device_descriptor(KnownDevice(device, __func__), static_cast<libusb_device_descriptor*>(descriptor)));
}
std::int32_t APS5_VABI sceUsbdGetStringDescriptor(void* deviceHandle, std::uint8_t descriptorIndex, std::uint16_t languageId, std::uint8_t* data, std::int32_t length) {
if (deviceHandle == nullptr || data == nullptr || length < 1) return SCE_USBD_ERROR_INVALID_ARG;
auto* handle = KnownHandle(deviceHandle, __func__);
if (length > 65535) length = 65535;
data[0] = 0;
return ToGuestError(libusb_get_string_descriptor(handle, descriptorIndex, languageId, data, length));
}
std::int32_t APS5_VABI sceUsbdKernelDriverActive(void* deviceHandle, std::int32_t interfaceNumber) {
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ToGuestError(libusb_kernel_driver_active(KnownHandle(deviceHandle, __func__), interfaceNumber));
}
std::int32_t APS5_VABI sceUsbdOpen(void* device, void** deviceHandle) {
if (device == nullptr) throw std::runtime_error(std::string(__func__) + ": null device");
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
*deviceHandle = nullptr;
libusb_device_handle* host = nullptr;
const int result = libusb_open(KnownDevice(device, __func__), &host);
if (result != 0) return ToGuestError(result);
auto& state = State();
std::lock_guard lock(state.mutex);
state.handles.insert(host);
*deviceHandle = host;
return 0;
}
void* APS5_VABI sceUsbdRefDevice(void* device) {
if (device == nullptr) return nullptr;
auto& state = State();
std::lock_guard lock(state.mutex);
auto* host = static_cast<libusb_device*>(device);
if (!state.devices.contains(host)) throw std::runtime_error(AbsentDevice(__func__));
AddDeviceReference(state, host);
return libusb_ref_device(host);
}
std::int32_t APS5_VABI sceUsbdReleaseInterface(void* deviceHandle, std::int32_t interfaceNumber) {
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ToGuestError(libusb_release_interface(KnownHandle(deviceHandle, __func__), interfaceNumber));
}
std::int32_t APS5_VABI sceUsbdResetDevice(void* deviceHandle) {
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ToGuestError(libusb_reset_device(KnownHandle(deviceHandle, __func__)));
}
std::int32_t APS5_VABI sceUsbdSetConfiguration(void* deviceHandle, std::int32_t configuration) {
if (deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ToGuestError(libusb_set_configuration(KnownHandle(deviceHandle, __func__), configuration));
}
std::int32_t APS5_VABI sceUsbdSubmitTransfer(UsbdTransfer* transfer) {
if (transfer == nullptr || transfer->deviceHandle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
auto* handle = KnownHandle(transfer->deviceHandle, __func__);
if ((transfer->flags & ~SUPPORTED_TRANSFER_FLAGS) != 0) throw std::runtime_error(std::string(__func__) + ": transfer flags " + std::to_string(transfer->flags) + " are not supported");
if (transfer->numIsoPackets < 0) return SCE_USBD_ERROR_INVALID_ARG;
auto* host = libusb_alloc_transfer(transfer->numIsoPackets);
if (host == nullptr) return ToGuestError(LIBUSB_ERROR_NO_MEM);
host->dev_handle = handle;
host->flags = transfer->flags;
host->endpoint = transfer->endpoint;
host->type = transfer->type;
host->timeout = transfer->timeout;
host->length = transfer->length;
host->buffer = transfer->buffer;
host->user_data = transfer;
host->callback = CompleteTransfer;
host->num_iso_packets = transfer->numIsoPackets;
for (int packet = 0; packet < transfer->numIsoPackets; ++packet) host->iso_packet_desc[packet].length = transfer->isoPacketDescriptors[packet].length;
auto& state = State();
{
std::lock_guard lock(state.mutex);
if (!state.inFlight.emplace(transfer, host).second) {
libusb_free_transfer(host);
return ToGuestError(LIBUSB_ERROR_BUSY);
}
}
const int result = libusb_submit_transfer(host);
if (result != 0) {
{
std::lock_guard lock(state.mutex);
state.inFlight.erase(transfer);
}
libusb_free_transfer(host);
}
return ToGuestError(result);
}
void APS5_VABI sceUsbdUnrefDevice(void* device) {
if (device == nullptr) return;
auto& state = State();
std::lock_guard lock(state.mutex);
auto* host = static_cast<libusb_device*>(device);
DropDeviceReference(state, host);
libusb_unref_device(host);
}
}