diff --git a/core/libs/prx/libSceUsbd/CMakeLists.txt b/core/libs/prx/libSceUsbd/CMakeLists.txt index d7acf60da81..f1ca6523020 100644 --- a/core/libs/prx/libSceUsbd/CMakeLists.txt +++ b/core/libs/prx/libSceUsbd/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(libSceUsbd SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libSceUsbd PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(libSceUsbd PRIVATE SDL2-static) configure_windows_unwind(libSceUsbd) if(BUILD_TESTING) diff --git a/core/libs/prx/libSceUsbd/Export.cpp b/core/libs/prx/libSceUsbd/Export.cpp index c5c238411d8..4b2db03c6ec 100644 --- a/core/libs/prx/libSceUsbd/Export.cpp +++ b/core/libs/prx/libSceUsbd/Export.cpp @@ -1,21 +1,162 @@ #include #include -#include "SceTypes.hpp" -#include "prx/libc/include/General.hpp" - -#include +#include +#include #include +#include #include +#include + +#include "SceTypes.hpp" +#include "prx/libc/include/General.hpp" namespace { constexpr std::int32_t SCE_USBD_ERROR_INVALID_ARG = static_cast(0x80240002); +constexpr std::int32_t SCE_USBD_ERROR_OTHER = static_cast(0x802400FF); +constexpr std::int32_t SCE_USBD_ERROR_NOT_SUPPORTED = static_cast(0x8024000C); + +constexpr int LIBUSB_ERROR_OTHER = -99; struct UsbdTimeval { std::int64_t seconds; std::int64_t microseconds; }; +struct SceUsbdDevice; +struct SceUsbdDeviceHandle; +struct SceUsbdTransfer; +struct SceUsbdConfigDescriptor; +struct SceUsbdControlSetup; +using SceUsbdTransferCallback = void (*)(SceUsbdTransfer*); + +struct SceUsbdDeviceDescriptor { + std::uint8_t Length; + std::uint8_t DescriptorType; + std::uint16_t BcdUsb; + std::uint8_t DeviceClass; + std::uint8_t DeviceSubClass; + std::uint8_t DeviceProtocol; + std::uint8_t MaxPacketSize0; + std::uint16_t IdVendor; + std::uint16_t IdProduct; + std::uint16_t BcdDevice; + std::uint8_t Manufacturer; + std::uint8_t Product; + std::uint8_t SerialNumber; + std::uint8_t ConfigurationCount; +}; + +struct UsbdApi { + void* library = nullptr; + void* context = nullptr; + bool available = false; + + int (*init)(void** context) = nullptr; + void (*exit)(void* context) = nullptr; + std::ptrdiff_t (*getDeviceList)(void* context, void*** list) = nullptr; + void (*freeDeviceList)(void** list, int unrefDevices) = nullptr; + int (*handleEventsTimeout)(void* context, void* timeval) = nullptr; + int (*eventHandlingOk)(void* context) = nullptr; + SceUsbdTransfer* (*allocTransfer)(int isoPackets) = nullptr; + void (*freeTransfer)(SceUsbdTransfer* transfer) = nullptr; + int (*submitTransfer)(SceUsbdTransfer* transfer) = nullptr; + int (*cancelTransfer)(SceUsbdTransfer* transfer) = nullptr; + void (*fillInterruptTransfer)(SceUsbdTransfer* transfer, SceUsbdDeviceHandle* handle, std::uint8_t endpoint, std::uint8_t* buffer, int length, SceUsbdTransferCallback callback, void* userData, unsigned int timeout) = nullptr; + int (*open)(SceUsbdDevice* device, SceUsbdDeviceHandle** handle) = nullptr; + void (*close)(SceUsbdDeviceHandle* handle) = nullptr; + SceUsbdDevice* (*refDevice)(SceUsbdDevice* device) = nullptr; + void (*unrefDevice)(SceUsbdDevice* device) = nullptr; + SceUsbdDevice* (*getDevice)(SceUsbdDeviceHandle* handle) = nullptr; + std::uint8_t (*getBusNumber)(SceUsbdDevice* device) = nullptr; + std::uint8_t (*getDeviceAddress)(SceUsbdDevice* device) = nullptr; + int (*claimInterface)(SceUsbdDeviceHandle* handle, int interfaceNumber) = nullptr; + int (*releaseInterface)(SceUsbdDeviceHandle* handle, int interfaceNumber) = nullptr; + int (*kernelDriverActive)(SceUsbdDeviceHandle* handle, int interfaceNumber) = nullptr; + int (*attachKernelDriver)(SceUsbdDeviceHandle* handle, int interfaceNumber) = nullptr; + int (*setConfiguration)(SceUsbdDeviceHandle* handle, int configuration) = nullptr; + int (*resetDevice)(SceUsbdDeviceHandle* handle) = nullptr; + int (*getDeviceDescriptor)(SceUsbdDevice* device, SceUsbdDeviceDescriptor* descriptor) = nullptr; + int (*getStringDescriptor)(SceUsbdDeviceHandle* handle, std::uint8_t index, std::uint16_t langid, std::uint8_t* data, int length) = nullptr; + int (*getConfigDescriptor)(SceUsbdDevice* device, std::uint8_t index, SceUsbdConfigDescriptor** descriptor) = nullptr; + int (*getActiveConfigDescriptor)(SceUsbdDevice* device, SceUsbdConfigDescriptor** descriptor) = nullptr; + void (*freeConfigDescriptor)(SceUsbdConfigDescriptor* descriptor) = nullptr; + int (*controlTransfer)(SceUsbdDeviceHandle* handle, std::uint8_t requestType, std::uint8_t request, std::uint16_t value, std::uint16_t index, std::uint8_t* data, std::uint16_t length, unsigned int timeout) = nullptr; +}; + +template +void Resolve(void* library, Function& function, const char* name) { + function = reinterpret_cast(SDL_LoadFunction(library, name)); +} + +UsbdApi Load() { + UsbdApi api; + const char* names[] = { +#ifdef _WIN32 + "libusb-1.0.dll", +#elif defined(__APPLE__) + "libusb-1.0.0.dylib", + "libusb-1.0.dylib", +#else + "libusb-1.0.so.0", + "libusb-1.0.so", +#endif + }; + for (const char* name : names) { + api.library = SDL_LoadObject(name); + if (api.library != nullptr) break; + } + if (api.library == nullptr) return api; + Resolve(api.library, api.init, "libusb_init"); + Resolve(api.library, api.exit, "libusb_exit"); + Resolve(api.library, api.getDeviceList, "libusb_get_device_list"); + Resolve(api.library, api.freeDeviceList, "libusb_free_device_list"); + Resolve(api.library, api.handleEventsTimeout, "libusb_handle_events_timeout"); + Resolve(api.library, api.eventHandlingOk, "libusb_event_handling_ok"); + Resolve(api.library, api.allocTransfer, "libusb_alloc_transfer"); + Resolve(api.library, api.freeTransfer, "libusb_free_transfer"); + Resolve(api.library, api.submitTransfer, "libusb_submit_transfer"); + Resolve(api.library, api.cancelTransfer, "libusb_cancel_transfer"); + Resolve(api.library, api.fillInterruptTransfer, "libusb_fill_interrupt_transfer"); + Resolve(api.library, api.open, "libusb_open"); + Resolve(api.library, api.close, "libusb_close"); + Resolve(api.library, api.refDevice, "libusb_ref_device"); + Resolve(api.library, api.unrefDevice, "libusb_unref_device"); + Resolve(api.library, api.getDevice, "libusb_get_device"); + Resolve(api.library, api.getBusNumber, "libusb_get_bus_number"); + Resolve(api.library, api.getDeviceAddress, "libusb_get_device_address"); + Resolve(api.library, api.claimInterface, "libusb_claim_interface"); + Resolve(api.library, api.releaseInterface, "libusb_release_interface"); + Resolve(api.library, api.kernelDriverActive, "libusb_kernel_driver_active"); + Resolve(api.library, api.attachKernelDriver, "libusb_attach_kernel_driver"); + Resolve(api.library, api.setConfiguration, "libusb_set_configuration"); + Resolve(api.library, api.resetDevice, "libusb_reset_device"); + Resolve(api.library, api.getDeviceDescriptor, "libusb_get_device_descriptor"); + Resolve(api.library, api.getStringDescriptor, "libusb_get_string_descriptor"); + Resolve(api.library, api.getConfigDescriptor, "libusb_get_config_descriptor"); + Resolve(api.library, api.getActiveConfigDescriptor, "libusb_get_active_config_descriptor"); + Resolve(api.library, api.freeConfigDescriptor, "libusb_free_config_descriptor"); + Resolve(api.library, api.controlTransfer, "libusb_control_transfer"); + api.available = api.init != nullptr && api.exit != nullptr && api.getDeviceList != nullptr && + api.freeDeviceList != nullptr && api.allocTransfer != nullptr && api.freeTransfer != nullptr; + return api; +} + +UsbdApi& Api() { + static UsbdApi api = Load(); + return api; +} + +std::int32_t Error(const int result) { + if (result == LIBUSB_ERROR_OTHER) return SCE_USBD_ERROR_OTHER; + if (result < 0) return static_cast(0x80240000u - static_cast(-static_cast(result))); + return static_cast(result); +} + +std::int32_t Unsupported() { + return Api().available ? SCE_USBD_ERROR_OTHER : SCE_USBD_ERROR_NOT_SUPPORTED; +} + void* g_emptyDeviceList[1] = {nullptr}; } @@ -23,152 +164,229 @@ void* g_emptyDeviceList[1] = {nullptr}; extern "C" { std::int32_t APS5_VABI sceUsbdInit() { + UsbdApi& api = Api(); + if (!api.available || api.context != nullptr) return 0; + void* context = nullptr; + const auto result = api.init(&context); + if (result != 0) return Error(result); + api.context = context; return 0; } void APS5_VABI sceUsbdExit() { + UsbdApi& api = Api(); + if (!api.available || api.context == nullptr) return; + api.exit(api.context); + api.context = nullptr; } std::int64_t APS5_VABI sceUsbdGetDeviceList(void*** list) { if (list == nullptr) return SCE_USBD_ERROR_INVALID_ARG; - *list = g_emptyDeviceList; - return 0; + UsbdApi& api = Api(); + if (!api.available) { + *list = g_emptyDeviceList; + return 0; + } + void** devices = nullptr; + const auto count = api.getDeviceList(api.context, &devices); + if (count < 0) return Error(static_cast(count)); + *list = devices; + return static_cast(count); } 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"); + if (list == nullptr) return; + UsbdApi& api = Api(); + if (!api.available || list == g_emptyDeviceList) { + if (list != g_emptyDeviceList) throw std::runtime_error(std::string(__func__) + ": list was not returned by sceUsbdGetDeviceList"); + return; + } + api.freeDeviceList(list, unrefDevices != 0 ? 1 : 0); } 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; + UsbdApi& api = Api(); + if (!api.available || api.context == nullptr || api.handleEventsTimeout == nullptr) { + std::this_thread::sleep_for(std::chrono::seconds(timeout->seconds) + std::chrono::microseconds(timeout->microseconds)); + return 0; + } + struct HostTimeval { + long seconds; + long microseconds; + } host{static_cast(timeout->seconds), static_cast(timeout->microseconds)}; + return Error(api.handleEventsTimeout(api.context, &host)); } -int APS5_VABI sceUsbdAllocTransfer() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdEventHandlingOk() { + UsbdApi& api = Api(); + if (!api.available || api.context == nullptr || api.eventHandlingOk == nullptr) return 1; + return api.eventHandlingOk(api.context); } -int APS5_VABI sceUsbdAttachKernelDriver() { - NotImplemented_nid_no_patch(__func__); - return 0; +SceUsbdTransfer* APS5_VABI sceUsbdAllocTransfer(std::int32_t isoPackets) { + UsbdApi& api = Api(); + if (!api.available) return nullptr; + return api.allocTransfer(isoPackets); } -int APS5_VABI sceUsbdCancelTransfer() { - NotImplemented_nid_no_patch(__func__); - return 0; +void APS5_VABI sceUsbdFreeTransfer(SceUsbdTransfer* transfer) { + UsbdApi& api = Api(); + if (!api.available || transfer == nullptr) return; + api.freeTransfer(transfer); } -int APS5_VABI sceUsbdCheckConnected() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdSubmitTransfer(SceUsbdTransfer* transfer) { + if (transfer == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.submitTransfer == nullptr) return Unsupported(); + return Error(api.submitTransfer(transfer)); } -int APS5_VABI sceUsbdClaimInterface() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdCancelTransfer(SceUsbdTransfer* transfer) { + if (transfer == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.cancelTransfer == nullptr) return Unsupported(); + return Error(api.cancelTransfer(transfer)); } -int APS5_VABI sceUsbdClose() { - NotImplemented_nid_no_patch(__func__); - return 0; +void APS5_VABI sceUsbdFillInterruptTransfer(SceUsbdTransfer* transfer, SceUsbdDeviceHandle* handle, std::uint8_t endpoint, std::uint8_t* buffer, std::int32_t length, SceUsbdTransferCallback callback, void* userData, std::uint32_t timeout) { + UsbdApi& api = Api(); + if (!api.available || api.fillInterruptTransfer == nullptr || transfer == nullptr || handle == nullptr) return; + api.fillInterruptTransfer(transfer, handle, endpoint, buffer, length, callback, userData, timeout); } -int APS5_VABI sceUsbdControlTransfer() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdOpen(SceUsbdDevice* device, SceUsbdDeviceHandle** handle) { + if (device == nullptr || handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.open == nullptr) return Unsupported(); + *handle = nullptr; + return Error(api.open(device, handle)); } -int APS5_VABI sceUsbdEventHandlingOk() { - NotImplemented_nid_no_patch(__func__); - return 0; +void APS5_VABI sceUsbdClose(SceUsbdDeviceHandle* handle) { + UsbdApi& api = Api(); + if (!api.available || api.close == nullptr || handle == nullptr) return; + api.close(handle); } -int APS5_VABI sceUsbdFillInterruptTransfer() { - NotImplemented_nid_no_patch(__func__); - return 0; +SceUsbdDevice* APS5_VABI sceUsbdRefDevice(SceUsbdDevice* device) { + UsbdApi& api = Api(); + if (!api.available || api.refDevice == nullptr || device == nullptr) return device; + return api.refDevice(device); } -int APS5_VABI sceUsbdFreeConfigDescriptor() { - NotImplemented_nid_no_patch(__func__); - return 0; +void APS5_VABI sceUsbdUnrefDevice(SceUsbdDevice* device) { + UsbdApi& api = Api(); + if (!api.available || api.unrefDevice == nullptr || device == nullptr) return; + api.unrefDevice(device); } -int APS5_VABI sceUsbdFreeTransfer() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint8_t APS5_VABI sceUsbdGetBusNumber(SceUsbdDevice* device) { + UsbdApi& api = Api(); + if (!api.available || api.getBusNumber == nullptr || device == nullptr) return 0; + return api.getBusNumber(device); } -int APS5_VABI sceUsbdGetActiveConfigDescriptor() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint8_t APS5_VABI sceUsbdGetDeviceAddress(SceUsbdDevice* device) { + UsbdApi& api = Api(); + if (!api.available || api.getDeviceAddress == nullptr || device == nullptr) return 0; + return api.getDeviceAddress(device); } -int APS5_VABI sceUsbdGetBusNumber() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdCheckConnected(SceUsbdDeviceHandle* handle) { + if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.getDevice == nullptr || api.getDeviceDescriptor == nullptr) return Unsupported(); + const auto device = api.getDevice(handle); + if (device == nullptr) return SCE_USBD_ERROR_OTHER; + SceUsbdDeviceDescriptor descriptor{}; + return Error(api.getDeviceDescriptor(device, &descriptor)); } -int APS5_VABI sceUsbdGetConfigDescriptor() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdClaimInterface(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber) { + if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.claimInterface == nullptr) return Unsupported(); + return Error(api.claimInterface(handle, interfaceNumber)); } -int APS5_VABI sceUsbdGetDeviceAddress() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdReleaseInterface(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber) { + if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.releaseInterface == nullptr) return Unsupported(); + return Error(api.releaseInterface(handle, interfaceNumber)); } -int APS5_VABI sceUsbdGetDeviceDescriptor() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdKernelDriverActive(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber) { + if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.kernelDriverActive == nullptr) return Unsupported(); + return Error(api.kernelDriverActive(handle, interfaceNumber)); } -int APS5_VABI sceUsbdGetStringDescriptor() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdAttachKernelDriver(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber) { + if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.attachKernelDriver == nullptr) return Unsupported(); + return Error(api.attachKernelDriver(handle, interfaceNumber)); } -int APS5_VABI sceUsbdKernelDriverActive() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdSetConfiguration(SceUsbdDeviceHandle* handle, std::int32_t configuration) { + if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.setConfiguration == nullptr) return Unsupported(); + return Error(api.setConfiguration(handle, configuration)); } -int APS5_VABI sceUsbdOpen() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdResetDevice(SceUsbdDeviceHandle* handle) { + if (handle == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.resetDevice == nullptr) return Unsupported(); + return Error(api.resetDevice(handle)); } -int APS5_VABI sceUsbdRefDevice() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdGetDeviceDescriptor(SceUsbdDevice* device, SceUsbdDeviceDescriptor* descriptor) { + if (device == nullptr || descriptor == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.getDeviceDescriptor == nullptr) return Unsupported(); + return Error(api.getDeviceDescriptor(device, descriptor)); } -int APS5_VABI sceUsbdReleaseInterface() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdGetStringDescriptor(SceUsbdDeviceHandle* handle, std::uint8_t index, std::uint16_t langid, std::uint8_t* data, std::int32_t length) { + if (handle == nullptr || data == nullptr || length <= 0) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.getStringDescriptor == nullptr) return Unsupported(); + return Error(api.getStringDescriptor(handle, index, langid, data, length)); } -int APS5_VABI sceUsbdResetDevice() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdGetConfigDescriptor(SceUsbdDevice* device, std::uint8_t index, SceUsbdConfigDescriptor** descriptor) { + if (device == nullptr || descriptor == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.getConfigDescriptor == nullptr) return Unsupported(); + *descriptor = nullptr; + return Error(api.getConfigDescriptor(device, index, descriptor)); } -int APS5_VABI sceUsbdSetConfiguration() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdGetActiveConfigDescriptor(SceUsbdDevice* device, SceUsbdConfigDescriptor** descriptor) { + if (device == nullptr || descriptor == nullptr) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.getActiveConfigDescriptor == nullptr) return Unsupported(); + *descriptor = nullptr; + return Error(api.getActiveConfigDescriptor(device, descriptor)); } -int APS5_VABI sceUsbdSubmitTransfer() { - NotImplemented_nid_no_patch(__func__); - return 0; +void APS5_VABI sceUsbdFreeConfigDescriptor(SceUsbdConfigDescriptor* descriptor) { + UsbdApi& api = Api(); + if (!api.available || api.freeConfigDescriptor == nullptr || descriptor == nullptr) return; + api.freeConfigDescriptor(descriptor); } -int APS5_VABI sceUsbdUnrefDevice() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::int32_t APS5_VABI sceUsbdControlTransfer(SceUsbdDeviceHandle* handle, std::uint8_t requestType, std::uint8_t request, std::uint16_t value, std::uint16_t index, std::uint8_t* data, std::int32_t length, std::uint32_t timeout) { + if (handle == nullptr || length < 0 || length > 65535 || (length > 0 && data == nullptr)) return SCE_USBD_ERROR_INVALID_ARG; + UsbdApi& api = Api(); + if (!api.available || api.controlTransfer == nullptr) return Unsupported(); + return Error(api.controlTransfer(handle, requestType, request, value, index, data, static_cast(length), timeout)); } } diff --git a/core/libs/tests/GuestUsbd.cpp b/core/libs/tests/GuestUsbd.cpp index 910810ec4c4..a540bce104e 100644 --- a/core/libs/tests/GuestUsbd.cpp +++ b/core/libs/tests/GuestUsbd.cpp @@ -13,6 +13,28 @@ struct UsbdTimeval { std::int64_t microseconds; }; +struct SceUsbdDevice; +struct SceUsbdDeviceHandle; +struct SceUsbdTransfer; +struct SceUsbdConfigDescriptor; + +struct SceUsbdDeviceDescriptor { + std::uint8_t Length; + std::uint8_t DescriptorType; + std::uint16_t BcdUsb; + std::uint8_t DeviceClass; + std::uint8_t DeviceSubClass; + std::uint8_t DeviceProtocol; + std::uint8_t MaxPacketSize0; + std::uint16_t IdVendor; + std::uint16_t IdProduct; + std::uint16_t BcdDevice; + std::uint8_t Manufacturer; + std::uint8_t Product; + std::uint8_t SerialNumber; + std::uint8_t ConfigurationCount; +}; + } extern "C" { @@ -21,12 +43,37 @@ void APS5_VABI sceUsbdExit(); std::int64_t APS5_VABI sceUsbdGetDeviceList(void*** list); void APS5_VABI sceUsbdFreeDeviceList(void** list, std::int32_t unrefDevices); std::int32_t APS5_VABI sceUsbdHandleEventsTimeout(const UsbdTimeval* timeout); -int APS5_VABI sceUsbdOpen(); +std::int32_t APS5_VABI sceUsbdEventHandlingOk(); +SceUsbdTransfer* APS5_VABI sceUsbdAllocTransfer(std::int32_t isoPackets); +void APS5_VABI sceUsbdFreeTransfer(SceUsbdTransfer* transfer); +std::int32_t APS5_VABI sceUsbdSubmitTransfer(SceUsbdTransfer* transfer); +std::int32_t APS5_VABI sceUsbdCancelTransfer(SceUsbdTransfer* transfer); +void APS5_VABI sceUsbdFillInterruptTransfer(SceUsbdTransfer* transfer, SceUsbdDeviceHandle* handle, std::uint8_t endpoint, std::uint8_t* buffer, std::int32_t length, void (*callback)(SceUsbdTransfer*), void* userData, std::uint32_t timeout); +std::int32_t APS5_VABI sceUsbdOpen(SceUsbdDevice* device, SceUsbdDeviceHandle** handle); +void APS5_VABI sceUsbdClose(SceUsbdDeviceHandle* handle); +SceUsbdDevice* APS5_VABI sceUsbdRefDevice(SceUsbdDevice* device); +void APS5_VABI sceUsbdUnrefDevice(SceUsbdDevice* device); +std::uint8_t APS5_VABI sceUsbdGetBusNumber(SceUsbdDevice* device); +std::uint8_t APS5_VABI sceUsbdGetDeviceAddress(SceUsbdDevice* device); +std::int32_t APS5_VABI sceUsbdCheckConnected(SceUsbdDeviceHandle* handle); +std::int32_t APS5_VABI sceUsbdClaimInterface(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber); +std::int32_t APS5_VABI sceUsbdReleaseInterface(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber); +std::int32_t APS5_VABI sceUsbdKernelDriverActive(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber); +std::int32_t APS5_VABI sceUsbdAttachKernelDriver(SceUsbdDeviceHandle* handle, std::int32_t interfaceNumber); +std::int32_t APS5_VABI sceUsbdSetConfiguration(SceUsbdDeviceHandle* handle, std::int32_t configuration); +std::int32_t APS5_VABI sceUsbdResetDevice(SceUsbdDeviceHandle* handle); +std::int32_t APS5_VABI sceUsbdGetDeviceDescriptor(SceUsbdDevice* device, SceUsbdDeviceDescriptor* descriptor); +std::int32_t APS5_VABI sceUsbdGetStringDescriptor(SceUsbdDeviceHandle* handle, std::uint8_t index, std::uint16_t langid, std::uint8_t* data, std::int32_t length); +std::int32_t APS5_VABI sceUsbdGetConfigDescriptor(SceUsbdDevice* device, std::uint8_t index, SceUsbdConfigDescriptor** descriptor); +std::int32_t APS5_VABI sceUsbdGetActiveConfigDescriptor(SceUsbdDevice* device, SceUsbdConfigDescriptor** descriptor); +void APS5_VABI sceUsbdFreeConfigDescriptor(SceUsbdConfigDescriptor* descriptor); +std::int32_t APS5_VABI sceUsbdControlTransfer(SceUsbdDeviceHandle* handle, std::uint8_t requestType, std::uint8_t request, std::uint16_t value, std::uint16_t index, std::uint8_t* data, std::int32_t length, std::uint32_t timeout); } namespace { constexpr std::int32_t invalidArgument = static_cast(0x80240002); +constexpr std::int32_t notSupported = static_cast(0x8024000C); void Require(bool condition, const char* message) { if (!condition) { @@ -40,8 +87,11 @@ void Require(bool condition, const char* message) { int main() { Require(sceUsbdInit() == 0, "initialization failed"); void** list = nullptr; - Require(sceUsbdGetDeviceList(&list) == 0, "a device was listed"); - Require(list != nullptr && list[0] == nullptr, "device list is not an empty null-terminated array"); + Require(sceUsbdGetDeviceList(&list) >= 0, "the device list reported a failure"); + Require(list != nullptr, "the device list is null"); + std::size_t devices = 0; + while (devices < 1024 && list[devices] != nullptr) ++devices; + Require(devices < 1024, "the device list is not null-terminated"); sceUsbdFreeDeviceList(list, 1); Require(sceUsbdGetDeviceList(nullptr) == invalidArgument, "null list accepted"); Require(sceUsbdHandleEventsTimeout(nullptr) == invalidArgument, "null timeout accepted"); @@ -51,13 +101,32 @@ int main() { const auto start = std::chrono::steady_clock::now(); Require(sceUsbdHandleEventsTimeout(&timeout) == 0, "event handling failed"); Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(45), "event handling returned before the timeout"); - bool threw = false; - try { - sceUsbdOpen(); - } catch (const std::runtime_error&) { - threw = true; + Require(sceUsbdEventHandlingOk() == 1, "event handling is not reported as ok"); + auto* transfer = sceUsbdAllocTransfer(0); + if (transfer != nullptr) { + sceUsbdFreeTransfer(transfer); + SceUsbdDeviceHandle* handle = reinterpret_cast(1); + Require(sceUsbdSubmitTransfer(nullptr) == invalidArgument, "a null transfer was submitted"); + Require(sceUsbdCancelTransfer(nullptr) == invalidArgument, "a null transfer was cancelled"); + Require(sceUsbdClaimInterface(nullptr, 0) == invalidArgument, "a null device handle claimed an interface"); + Require(sceUsbdResetDevice(nullptr) == invalidArgument, "a null device handle was reset"); + Require(sceUsbdGetStringDescriptor(nullptr, 1, 0x409, nullptr, 8) == invalidArgument, "a null string buffer was accepted"); + Require(sceUsbdOpen(nullptr, &handle) == invalidArgument, "a null device was opened"); + Require(sceUsbdCheckConnected(nullptr) == invalidArgument, "a null device handle reports a connection"); + Require(sceUsbdGetBusNumber(nullptr) == 0, "a null device has a bus number"); + sceUsbdFreeTransfer(nullptr); + sceUsbdClose(nullptr); + sceUsbdUnrefDevice(nullptr); + sceUsbdFreeConfigDescriptor(nullptr); + } else { + Require(sceUsbdClaimInterface(reinterpret_cast(1), 0) == notSupported, "a device call without a host USB library did not report it as unsupported"); + Require(sceUsbdSubmitTransfer(reinterpret_cast(1)) == notSupported, "a transfer without a host USB library did not report it as unsupported"); } - Require(threw, "opening a device did not throw"); + SceUsbdDeviceDescriptor descriptor{}; + Require(sceUsbdGetDeviceDescriptor(nullptr, &descriptor) == invalidArgument, "a null device returned a descriptor"); + Require(sceUsbdGetDeviceDescriptor(reinterpret_cast(1), nullptr) == invalidArgument, "a null descriptor pointer was accepted"); + Require(sceUsbdControlTransfer(nullptr, 0, 0, 0, 0, nullptr, 0, 0) == invalidArgument, "a null device handle ran a control transfer"); + Require(sceUsbdControlTransfer(reinterpret_cast(1), 0, 0, 0, 0, nullptr, -1, 0) == invalidArgument, "a negative control transfer length was accepted"); sceUsbdExit(); std::puts("USBD tests passed"); return 0; diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index f4812dd2296..4fc3ccd0007 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -21,7 +21,7 @@ Throughout the project, every function at every stage either **does exactly what - [libSceMbus](../../core/libs/prx/libSceMbus/Export.cpp) implements only the no-device path: initialization and termination succeed, no device events are reported and device queries return no entries - [libSceAudioPropagation](../../core/libs/prx/libSceAudioPropagation/Export.cpp) implements only the no-acoustic-propagation path: the system keeps a 16-byte header in the title's CPU memory (no GPU memory), rooms, portals, sources and materials are tracked handles, attributes are validated and have no effect, no rays are requested and sources report no audio paths. Ray results, path calculation and audio paths are accepted only when empty and throw otherwise; `sceAudioPropagationSourceRender` writes silence (zeroes) to each source's output buffer for the given size, without a dry signal. Objects still alive when their system is destroyed become unusable, and their later destroy or unregister is a no-op: PPSA21567 releases its reference-counted room, portal and source owners just before `sceAudioPropagationSystemDestroy`, so the order for every object is not proven. Occlusion, reflections and reverb from level geometry are absent; in-game behaviour remains unverified. - [libSceNpCommerce](../../core/libs/prx/libSceNpCommerce/Export.cpp) - the PS Store icon show/hide calls and `sceNpCommerceSetPsStoreIconLayout` do nothing -- [libSceUsbd](../../core/libs/prx/libSceUsbd/Export.cpp) implements only the no-device path: `sceUsbdInit` and `sceUsbdExit` succeed without a host USB context, the device list is always empty and event handling waits for its timeout; calls that need a device throw +- [libSceUsbd](../../core/libs/prx/libSceUsbd/Export.cpp) resolves the host's libusb at runtime: with it present the device calls are its API, without it every call that needs a device reports `SCE_USBD_ERROR_NOT_SUPPORTED` and the device list is empty. Enumeration, transfer allocation and the argument contracts are exercised by `guest_usbd`; no transfer, descriptor read or interface claim has been run against a real USB device, and the guest signatures come from the PS4 SDK header and shadPS4 rather than from the console - [libSceSystemService](../../core/libs/prx/libSceSystemService/Export.cpp) - `sceSystemServicePowerTick`, `sceSystemServiceReportAbnormalTermination`, `sceSystemServiceDisableMusicPlayer` and `sceSystemServiceReenableMusicPlayer` return success and do nothing: the host has no auto power-off timer to reset, no system crash reporter to notify and no system music player to pause or resume - [libSceBluetoothHid](../../core/libs/prx/libSceBluetoothHid/Export.cpp) implements only the no-device path: initialization and callback and device registration succeed, the callback is never called because no device connects, and calls that need a connected device throw - [sceVideoOutOpen](../../core/libs/prx/libSceVideoOut/src/Output.cpp) validates the priority and CPU affinity that the open param requests for the VideoOut service thread but does not apply them: the port's present and vblank threads are host threads, and guest priorities and affinities do not reach host scheduling