Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@
[submodule "3rdparty/libjpeg-turbo"]
path = 3rdparty/libjpeg-turbo
url = https://github.com/libjpeg-turbo/libjpeg-turbo.git
[submodule "3rdparty/libusb"]
path = 3rdparty/libusb
url = https://github.com/libusb/libusb-cmake.git
1 change: 1 addition & 0 deletions 3rdparty/libusb
Submodule libusb added at c8477c
21 changes: 21 additions & 0 deletions core/libs/prx/libSceUsbd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
set(LIBUSB_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(LIBUSB_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(LIBUSB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(LIBUSB_INSTALL_TARGETS OFF CACHE BOOL "" FORCE)
set(LIBUSB_ENABLE_UDEV OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/libusb ${CMAKE_CURRENT_BINARY_DIR}/libusb EXCLUDE_FROM_ALL)
set_target_properties(usb-1.0 PROPERTIES POSITION_INDEPENDENT_CODE ON)

add_library(libSceUsbd SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libSceUsbd PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceUsbd PRIVATE usb-1.0)
if(MINGW OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_options(libSceUsbd PRIVATE LINKER:--exclude-libs,ALL)
endif()
configure_windows_unwind(libSceUsbd)

if(BUILD_TESTING)
add_test_executable(guest_usbd_tests ${LIBS_INCLUDE_DIR}/tests/GuestUsbd.cpp)
target_include_directories(guest_usbd_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_include_directories(guest_usbd_tests SYSTEM PRIVATE $<TARGET_PROPERTY:usb-1.0,INTERFACE_INCLUDE_DIRECTORIES>)
target_link_libraries(guest_usbd_tests PRIVATE libSceUsbd libc)
configure_windows_unwind(guest_usbd_tests)
add_test(NAME guest_usbd COMMAND guest_usbd_tests)
add_test_executable(guest_usbd_control_tests ${LIBS_INCLUDE_DIR}/tests/GuestUsbdControl.cpp Export.cpp)
target_include_directories(guest_usbd_control_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_usbd_control_tests PRIVATE usb-1.0 libc)
target_link_options(guest_usbd_control_tests PRIVATE LINKER:--wrap=libusb_control_transfer)
configure_windows_unwind(guest_usbd_control_tests)
add_test(NAME guest_usbd_control COMMAND guest_usbd_control_tests)
if(WIN32)
set_tests_properties(guest_usbd PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceUsbd>")
set_tests_properties(guest_usbd_control PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
202 changes: 119 additions & 83 deletions core/libs/prx/libSceUsbd/Export.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include <limits>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <libusb.h>
#include "prx/libc/include/General.hpp"

#include <chrono>
#include <string>
#include <thread>

namespace {

constexpr std::int32_t SCE_USBD_ERROR_INVALID_ARG = static_cast<std::int32_t>(0x80240002);
Expand All @@ -16,159 +16,195 @@ struct UsbdTimeval {
std::int64_t microseconds;
};

void* g_emptyDeviceList[1] = {nullptr};
using UsbdTransferCallback = void (APS5_VABI *)(libusb_transfer*);

std::mutex contextMutex;
std::shared_ptr<libusb_context> context;
std::size_t initializationCount = 0;

std::int32_t ConvertError(int result) {
if (result >= 0) return result;
if (result == LIBUSB_ERROR_OTHER) return static_cast<std::int32_t>(0x802400ff);
return static_cast<std::int32_t>(0x80240000u - static_cast<std::uint32_t>(result));
}

std::shared_ptr<libusb_context> GetContext() {
std::lock_guard lock(contextMutex);
if (!context) throw std::runtime_error("libSceUsbd: library is not initialized");
return context;
}

}

extern "C" {

std::int32_t APS5_VABI sceUsbdInit() {
std::lock_guard lock(contextMutex);
if (context) {
++initializationCount;
return 0;
}
libusb_context* created = nullptr;
const int result = libusb_init_context(&created, nullptr, 0);
if (result < 0) return ConvertError(result);
context = std::shared_ptr<libusb_context>(created, libusb_exit);
initializationCount = 1;
return 0;
}

void APS5_VABI sceUsbdExit() {
std::shared_ptr<libusb_context> released;
{
std::lock_guard lock(contextMutex);
if (initializationCount != 0 && --initializationCount == 0) released.swap(context);
}
}

std::int64_t APS5_VABI sceUsbdGetDeviceList(void*** list) {
std::int64_t APS5_VABI sceUsbdGetDeviceList(libusb_device*** list) {
if (list == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
*list = g_emptyDeviceList;
return 0;
const auto current = GetContext();
const auto result = libusb_get_device_list(current.get(), list);
return result < 0 ? ConvertError(static_cast<int>(result)) : result;
}

void APS5_VABI sceUsbdFreeDeviceList(void** list, std::int32_t unrefDevices) {
(void)unrefDevices;
if (list != nullptr && list != g_emptyDeviceList) throw std::runtime_error(std::string(__func__) + ": list was not returned by sceUsbdGetDeviceList");
void APS5_VABI sceUsbdFreeDeviceList(libusb_device** list, std::int32_t unrefDevices) {
libusb_free_device_list(list, 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;
std::this_thread::sleep_for(std::chrono::seconds(timeout->seconds) + std::chrono::microseconds(timeout->microseconds));
return 0;
timeval native{};
if (timeout->seconds > std::numeric_limits<decltype(native.tv_sec)>::max()) {
throw std::runtime_error("sceUsbdHandleEventsTimeout: timeout exceeds host timeval range");
}
native.tv_sec = static_cast<decltype(native.tv_sec)>(timeout->seconds);
native.tv_usec = static_cast<decltype(native.tv_usec)>(timeout->microseconds);
const auto current = GetContext();
return ConvertError(libusb_handle_events_timeout(current.get(), &native));
}

int APS5_VABI sceUsbdAllocTransfer() {
NotImplemented_nid_no_patch(__func__);
return 0;
libusb_transfer* APS5_VABI sceUsbdAllocTransfer(int isoPackets) {
if (isoPackets < 0) return nullptr;
return libusb_alloc_transfer(isoPackets);
}

int APS5_VABI sceUsbdAttachKernelDriver() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdAttachKernelDriver(libusb_device_handle* handle, int interfaceNumber) {
if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_attach_kernel_driver(handle, interfaceNumber));
}

int APS5_VABI sceUsbdCancelTransfer() {
int APS5_VABI sceUsbdCancelTransfer(libusb_transfer* transfer) {
(void)transfer;
NotImplemented_nid_no_patch(__func__);
return 0;
}

int APS5_VABI sceUsbdCheckConnected() {
int APS5_VABI sceUsbdCheckConnected(libusb_device_handle* handle) {
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}

int APS5_VABI sceUsbdClaimInterface() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdClaimInterface(libusb_device_handle* handle, int interfaceNumber) {
if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_claim_interface(handle, interfaceNumber));
}

int APS5_VABI sceUsbdClose() {
NotImplemented_nid_no_patch(__func__);
return 0;
void APS5_VABI sceUsbdClose(libusb_device_handle* handle) {
libusb_close(handle);
}

int APS5_VABI sceUsbdControlTransfer() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdControlTransfer(libusb_device_handle* handle, std::uint8_t requestType, std::uint8_t request, std::uint16_t value, std::uint16_t index, unsigned char* data, std::int32_t length, std::uint32_t timeout) {
if (handle == nullptr || length < 0 || length > UINT16_MAX || (length != 0 && data == nullptr)) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_control_transfer(handle, requestType, request, value, index, data, static_cast<std::uint16_t>(length), timeout));
}

int APS5_VABI sceUsbdEventHandlingOk() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdEventHandlingOk() {
const auto current = GetContext();
return libusb_event_handling_ok(current.get());
}

int APS5_VABI sceUsbdFillInterruptTransfer() {
NotImplemented_nid_no_patch(__func__);
return 0;
void APS5_VABI sceUsbdFillInterruptTransfer(libusb_transfer* transfer, libusb_device_handle* handle, std::uint8_t endpoint, unsigned char* buffer, int length, UsbdTransferCallback callback, void* userData, std::uint32_t timeout) {
if (transfer == nullptr) APS5_INVALID_ARG_EX;
libusb_fill_interrupt_transfer(transfer, handle, endpoint, buffer, length, reinterpret_cast<libusb_transfer_cb_fn>(callback), userData, timeout);
}

int APS5_VABI sceUsbdFreeConfigDescriptor() {
NotImplemented_nid_no_patch(__func__);
return 0;
void APS5_VABI sceUsbdFreeConfigDescriptor(libusb_config_descriptor* descriptor) {
libusb_free_config_descriptor(descriptor);
}

int APS5_VABI sceUsbdFreeTransfer() {
NotImplemented_nid_no_patch(__func__);
return 0;
void APS5_VABI sceUsbdFreeTransfer(libusb_transfer* transfer) {
libusb_free_transfer(transfer);
}

int APS5_VABI sceUsbdGetActiveConfigDescriptor() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdGetActiveConfigDescriptor(libusb_device* device, libusb_config_descriptor** config) {
if (device == nullptr || config == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_get_active_config_descriptor(device, config));
}

int APS5_VABI sceUsbdGetBusNumber() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::uint8_t APS5_VABI sceUsbdGetBusNumber(libusb_device* device) {
if (device == nullptr) APS5_INVALID_ARG_EX;
return libusb_get_bus_number(device);
}

int APS5_VABI sceUsbdGetConfigDescriptor() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdGetConfigDescriptor(libusb_device* device, std::uint8_t index, libusb_config_descriptor** config) {
if (device == nullptr || config == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_get_config_descriptor(device, index, config));
}

int APS5_VABI sceUsbdGetDeviceAddress() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::uint8_t APS5_VABI sceUsbdGetDeviceAddress(libusb_device* device) {
if (device == nullptr) APS5_INVALID_ARG_EX;
return libusb_get_device_address(device);
}

int APS5_VABI sceUsbdGetDeviceDescriptor() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdGetDeviceDescriptor(libusb_device* device, libusb_device_descriptor* descriptor) {
if (device == nullptr || descriptor == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_get_device_descriptor(device, descriptor));
}

int APS5_VABI sceUsbdGetStringDescriptor() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdGetStringDescriptor(libusb_device_handle* handle, std::uint8_t index, std::uint16_t language, unsigned char* data, int length) {
if (handle == nullptr || data == nullptr || length < 0 || length > UINT16_MAX) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_get_string_descriptor(handle, index, language, data, length));
}

int APS5_VABI sceUsbdKernelDriverActive() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdKernelDriverActive(libusb_device_handle* handle, int interfaceNumber) {
if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_kernel_driver_active(handle, interfaceNumber));
}

int APS5_VABI sceUsbdOpen() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdOpen(libusb_device* device, libusb_device_handle** handle) {
if (device == nullptr || handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_open(device, handle));
}

int APS5_VABI sceUsbdRefDevice() {
NotImplemented_nid_no_patch(__func__);
return 0;
libusb_device* APS5_VABI sceUsbdRefDevice(libusb_device* device) {
if (device == nullptr) return nullptr;
return libusb_ref_device(device);
}

int APS5_VABI sceUsbdReleaseInterface() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdReleaseInterface(libusb_device_handle* handle, int interfaceNumber) {
if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_release_interface(handle, interfaceNumber));
}

int APS5_VABI sceUsbdResetDevice() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdResetDevice(libusb_device_handle* handle) {
if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_reset_device(handle));
}

int APS5_VABI sceUsbdSetConfiguration() {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int32_t APS5_VABI sceUsbdSetConfiguration(libusb_device_handle* handle, int configuration) {
if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG;
return ConvertError(libusb_set_configuration(handle, configuration));
}

int APS5_VABI sceUsbdSubmitTransfer() {
int APS5_VABI sceUsbdSubmitTransfer(libusb_transfer* transfer) {
(void)transfer;
NotImplemented_nid_no_patch(__func__);
return 0;
}

int APS5_VABI sceUsbdUnrefDevice() {
NotImplemented_nid_no_patch(__func__);
return 0;
void APS5_VABI sceUsbdUnrefDevice(libusb_device* device) {
libusb_unref_device(device);
}

}
Loading
Loading