diff --git a/src/core/libraries/usbd/usb_device.cpp b/src/core/libraries/usbd/usb_device.cpp new file mode 100644 index 0000000000..661f326582 --- /dev/null +++ b/src/core/libraries/usbd/usb_device.cpp @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include "common/assert.h" +#include "common/logging/log.h" +#include "usb_device.h" +#include "usbd_impl.h" + +namespace Libraries::Usbd { + +extern void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer); + +////////////////////////////////////////////////////////////////// +// ALL DEVICES /////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +UsbDevice::UsbDevice(const std::array& location) { + this->location = location; +} + +void UsbDevice::get_location(u8* location) const { + memcpy(location, this->location.data(), 7); +} + +void UsbDevice::read_descriptors() {} + +u32 UsbDevice::get_configuration(u8* buf) { + *buf = current_config; + return sizeof(u8); +} + +bool UsbDevice::set_configuration(u8 cfg_num) { + current_config = cfg_num; + return true; +} + +bool UsbDevice::set_interface(u8 int_num) { + current_interface = int_num; + return true; +} + +u64 UsbDevice::get_timestamp() { + return 0; /*(get_system_time() - Emu.GetPauseTime())*/ +} + +UsbDevicePassthrough::UsbDevicePassthrough(libusb_device* _device, libusb_device_descriptor& desc, + const std::array& location) + : UsbDevice(location), lusb_device(_device) { + device = UsbDescriptorNode( + USB_DESCRIPTOR_DEVICE, + UsbDeviceDescriptor{desc.bcdUSB, desc.bDeviceClass, desc.bDeviceSubClass, + desc.bDeviceProtocol, desc.bMaxPacketSize0, desc.idVendor, + desc.idProduct, desc.bcdDevice, desc.iManufacturer, desc.iProduct, + desc.iSerialNumber, desc.bNumConfigurations}); +} + +UsbDevicePassthrough::~UsbDevicePassthrough() { + if (lusb_handle) { + libusb_release_interface(lusb_handle, 0); + libusb_close(lusb_handle); + } + + if (lusb_device) { + libusb_unref_device(lusb_device); + } +} + +void UsbDevicePassthrough::send_libusb_transfer(libusb_transfer* transfer) { + while (true) { + auto res = libusb_submit_transfer(transfer); + switch (res) { + case LIBUSB_SUCCESS: + return; + case LIBUSB_ERROR_BUSY: + continue; + default: { + LOG_ERROR(Lib_Usbd, "Unexpected error from libusb_submit_transfer: %d(%s)", res, + libusb_error_name(res)); + return; + } + } + } +} + +int UsbDevicePassthrough::open_device() { + int err = libusb_open(lusb_device, &lusb_handle); + if (err != LIBUSB_SUCCESS) { + return err; + } +#ifdef __linux__ + libusb_set_auto_detach_kernel_driver(lusb_handle, true); +#endif + return LIBUSB_SUCCESS; +} + +void UsbDevicePassthrough::read_descriptors() { + // Directly getting configuration descriptors from the device instead of going through libusb + // parsing functions as they're not needed + for (u8 index = 0; index < device._device.bNumConfigurations; index++) { + u8 buf[1000]; + int ssize = libusb_control_transfer( + lusb_handle, + +LIBUSB_ENDPOINT_IN | +LIBUSB_REQUEST_TYPE_STANDARD | +LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_GET_DESCRIPTOR, 0x0200 | index, 0, buf, 1000, 0); + if (ssize < 0) { + LOG_ERROR(Lib_Usbd, "Couldn't get the config from the device: %d(%s)", ssize, + libusb_error_name(ssize)); + continue; + } + + // Minimalistic parse + auto& conf = device.add_node(UsbDescriptorNode(buf[0], buf[1], &buf[2])); + + for (int index = buf[0]; index < ssize;) { + conf.add_node(UsbDescriptorNode(buf[index], buf[index + 1], &buf[index + 2])); + index += buf[index]; + } + } +} + +u32 UsbDevicePassthrough::get_configuration(u8* buf) { + return (libusb_get_configuration(lusb_handle, reinterpret_cast(buf)) == LIBUSB_SUCCESS) + ? sizeof(u8) + : 0; +}; + +bool UsbDevicePassthrough::set_configuration(u8 cfg_num) { + UsbDevice::set_configuration(cfg_num); + return (libusb_set_configuration(lusb_handle, cfg_num) == LIBUSB_SUCCESS); +}; + +bool UsbDevicePassthrough::set_interface(u8 int_num) { + UsbDevice::set_interface(int_num); + return (libusb_claim_interface(lusb_handle, int_num) == LIBUSB_SUCCESS); +} + +void UsbDevicePassthrough::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, + [[maybe_unused]] u16 wLength, u32 buf_size, u8* buf, + UsbTransfer* transfer) { + if (transfer->setup_buf.size() < buf_size + LIBUSB_CONTROL_SETUP_SIZE) + transfer->setup_buf.resize(buf_size + LIBUSB_CONTROL_SETUP_SIZE); + + transfer->control_destbuf = (bmRequestType & LIBUSB_ENDPOINT_IN) ? buf : nullptr; + + libusb_fill_control_setup(transfer->setup_buf.data(), bmRequestType, bRequest, wValue, wIndex, + buf_size); + memcpy(transfer->setup_buf.data() + LIBUSB_CONTROL_SETUP_SIZE, buf, buf_size); + libusb_fill_control_transfer(transfer->transfer, lusb_handle, transfer->setup_buf.data(), + nullptr, transfer, 0); + send_libusb_transfer(transfer->transfer); +} + +void UsbDevicePassthrough::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, + UsbTransfer* transfer) { + libusb_fill_interrupt_transfer(transfer->transfer, lusb_handle, endpoint, buf, buf_size, + nullptr, transfer, 0); + send_libusb_transfer(transfer->transfer); +} + +void UsbDevicePassthrough::isochronous_transfer(UsbTransfer* transfer) { + // TODO actual endpoint + // TODO actual size? + libusb_fill_iso_transfer(transfer->transfer, lusb_handle, 0x81, + static_cast(transfer->iso_request.buf), 0xFFFF, + transfer->iso_request.num_packets, callback_transfer, transfer, 0); + + for (u32 index = 0; index < transfer->iso_request.num_packets; index++) { + transfer->transfer->iso_packet_desc[index].length = transfer->iso_request.packets[index]; + } + + send_libusb_transfer(transfer->transfer); +} + +UsbDeviceEmulated::UsbDeviceEmulated(const std::array& location) : UsbDevice(location) {} + +UsbDeviceEmulated::UsbDeviceEmulated(const UsbDeviceDescriptor& _device, + const std::array& location) + : UsbDevice(location) { + device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, _device); +} + +int UsbDeviceEmulated::open_device() { + return LIBUSB_SUCCESS; +} + +u32 UsbDeviceEmulated::get_descriptor(u8 type, u8 index, u8* buf, u32 buf_size) { + if (!buf) { + return 0; + } + + std::array header; + header = {static_cast(header.size()), type}; + + u32 expected_count = std::min(static_cast(header.size()), buf_size); + std::memcpy(buf, header.data(), expected_count); + + if (expected_count < header.size()) + return expected_count; + + switch (type) { + case USB_DESCRIPTOR_DEVICE: { + buf[0] = device.bLength; + expected_count = std::min(device.bLength, static_cast(buf_size)); + std::memcpy(buf + header.size(), device.data, expected_count - header.size()); + break; + } + case USB_DESCRIPTOR_CONFIG: { + if (index < device.subnodes.size()) { + buf[0] = device.subnodes[index].bLength; + expected_count = std::min(device.subnodes[index].bLength, static_cast(buf_size)); + std::memcpy(buf + header.size(), device.subnodes[index].data, + expected_count - header.size()); + } + break; + } + case USB_DESCRIPTOR_STRING: { + if (index < strings.size() + 1) { + if (index == 0) { + constexpr u8 len = static_cast(sizeof(u16) + header.size()); + buf[0] = len; + expected_count = std::min(len, static_cast(buf_size)); + constexpr u16 langid = 0x0409; // English (United States) + std::memcpy(buf + header.size(), &langid, expected_count - header.size()); + } else { + std::wstring_convert, char16_t> converter; + const std::u16string u16str = converter.from_bytes(strings[index - 1]); + const u8 len = static_cast(std::min(u16str.size() * sizeof(u16) + header.size(), + static_cast(0xFF))); + buf[0] = len; + expected_count = std::min(len, static_cast(std::min(255, buf_size))); + std::memcpy(buf + header.size(), u16str.data(), expected_count - header.size()); + } + } + break; + } + default: + LOG_ERROR(Lib_Usbd, "Unhandled DescriptorType"); + break; + } + + return expected_count; +} + +u32 UsbDeviceEmulated::get_status(bool self_powered, bool remote_wakeup, u8* buf, u32 buf_size) { + const u32 expected_count = buf ? std::min(sizeof(u16), buf_size) : 0; + const u16 device_status = static_cast(self_powered) | static_cast(remote_wakeup) << 1; + std::memcpy(buf, &device_status, expected_count); + return expected_count; +} + +void UsbDeviceEmulated::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, + u16 /*wLength*/, u32 buf_size, u8* buf, + UsbTransfer* transfer) { + transfer->fake = true; + transfer->expected_count = buf_size; + transfer->expected_result = LIBUSB_SUCCESS; + transfer->expected_time = UsbDevice::get_timestamp() + 100; + + switch (bmRequestType) { + case 0U /*silences warning*/ | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | + LIBUSB_RECIPIENT_DEVICE: // 0x00 + switch (bRequest) { + case LIBUSB_REQUEST_SET_CONFIGURATION: + UsbDevice::set_configuration(static_cast(wValue)); + break; + default: + LOG_ERROR(Lib_Usbd, "Unhandled control transfer({}): {}", bmRequestType, bRequest); + break; + } + break; + case 0U /*silences warning*/ | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | + LIBUSB_RECIPIENT_INTERFACE: // 0x01 + switch (bRequest) { + case LIBUSB_REQUEST_SET_INTERFACE: + UsbDevice::set_interface(static_cast(wIndex)); + break; + default: + LOG_ERROR(Lib_Usbd, "Unhandled control transfer({}): {}", bmRequestType, bRequest); + break; + } + break; + case 0U /*silences warning*/ | LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | + LIBUSB_RECIPIENT_DEVICE: // 0x80 + switch (bRequest) { + case LIBUSB_REQUEST_GET_STATUS: + transfer->expected_count = get_status(false, false, buf, buf_size); + break; + case LIBUSB_REQUEST_GET_DESCRIPTOR: + transfer->expected_count = get_descriptor(wValue >> 8, wValue & 0xFF, buf, buf_size); + break; + case LIBUSB_REQUEST_GET_CONFIGURATION: + transfer->expected_count = get_configuration(buf); + break; + default: + LOG_ERROR(Lib_Usbd, "Unhandled control transfer({}): {}", bmRequestType, bRequest); + break; + } + break; + default: + LOG_ERROR(Lib_Usbd, "Unhandled control transfer: {}", bmRequestType); + break; + } +} + +// Temporarily +#ifndef _MSC_VER +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +void UsbDeviceEmulated::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, + UsbTransfer* transfer) {} + +void UsbDeviceEmulated::isochronous_transfer(UsbTransfer* transfer) {} + +void UsbDeviceEmulated::add_string(std::string str) { + strings.emplace_back(std::move(str)); +} +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/usb_device.h b/src/core/libraries/usbd/usb_device.h new file mode 100644 index 0000000000..8ffc4bedc7 --- /dev/null +++ b/src/core/libraries/usbd/usb_device.h @@ -0,0 +1,237 @@ +#pragma once +#include + +#include + +namespace Libraries::Usbd { + +struct UsbTransfer; + +// Usb descriptors +enum : u8 { + USB_DESCRIPTOR_DEVICE = 0x01, + USB_DESCRIPTOR_CONFIG = 0x02, + USB_DESCRIPTOR_STRING = 0x03, + USB_DESCRIPTOR_INTERFACE = 0x04, + USB_DESCRIPTOR_ENDPOINT = 0x05, + USB_DESCRIPTOR_HID = 0x21, + USB_DESCRIPTOR_ACI = 0x24, + USB_DESCRIPTOR_ENDPOINT_ASI = 0x25, +}; + +struct UsbDeviceDescriptor { + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u16 idVendor; + u16 idProduct; + u16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +}; + +struct UsbDeviceConfiguration { + u16 wTotalLength; + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; +}; + +struct UsbDeviceInterface { + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; +}; + +struct UsbDeviceEndpoint { + u8 bEndpointAddress; + u8 bmAttributes; + u16 wMaxPacketSize; + u8 bInterval; +}; + +struct UsbDeviceHID { + u16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bDescriptorType; + u16 wDescriptorLength; +}; + +struct UsbDeviceRequest { + u8 bmRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +}; + +struct UsbDeviceIsoRequest { + void* buf; + u32 start_frame; + u32 num_packets; + u16 packets[8]; +}; + +struct UsbTransfer { + u32 assigned_number = 0; + u32 transfer_id = 0; + + s32 result = 0; + u32 count = 0; + UsbDeviceIsoRequest iso_request{}; + + std::vector setup_buf; + libusb_transfer* transfer = nullptr; + bool busy = false; + + // For control transfers + u8* control_destbuf = nullptr; + + // For fake transfers + bool fake = false; + u64 expected_time = 0; + s32 expected_result = 0; + u32 expected_count = 0; +}; + +// Usb descriptor helper +struct UsbDescriptorNode { + u8 bLength{}; + u8 bDescriptorType{}; + + union { + UsbDeviceDescriptor _device; + UsbDeviceConfiguration _configuration; + UsbDeviceInterface _interface; + UsbDeviceEndpoint _endpoint; + UsbDeviceHID _hid; + u8 data[0xFF]{}; + }; + + std::vector subnodes; + + UsbDescriptorNode() {} + template + UsbDescriptorNode(u8 _bDescriptorType, const T& _data) + : bLength(sizeof(T) + 2), bDescriptorType(_bDescriptorType) { + memcpy(data, &_data, sizeof(T)); + } + UsbDescriptorNode(u8 _bLength, u8 _bDescriptorType, u8* _data) + : bLength(_bLength), bDescriptorType(_bDescriptorType) { + memcpy(data, _data, _bLength - 2); + } + + UsbDescriptorNode& add_node(const UsbDescriptorNode& newnode) { + subnodes.push_back(newnode); + return subnodes.back(); + } + + u32 get_size() const { + u32 nodesize = bLength; + for (const auto& node : subnodes) { + nodesize += node.get_size(); + } + return nodesize; + } + + u32 write_data(u8* ptr, u32 max_size) const { + u32 size = std::min(bLength, max_size); + memcpy(ptr, this, size); + for (const auto& node : subnodes) { + const u32 remaining = max_size - size; + if (remaining == 0) + break; + size += node.write_data(ptr + size, remaining); + } + return size; + } +}; + +class UsbDevice { +public: + UsbDevice(const std::array& location); + virtual ~UsbDevice() = default; + + virtual int open_device() = 0; + + void get_location(u8* location) const; + virtual void read_descriptors(); + + virtual u32 get_configuration(u8* buf); + virtual bool set_configuration(u8 cfg_num); + virtual bool set_interface(u8 int_num); + + virtual void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, + u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) = 0; + virtual void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) = 0; + virtual void isochronous_transfer(UsbTransfer* transfer) = 0; + +public: + // base device descriptor, every other descriptor is a subnode + UsbDescriptorNode device; + +protected: + u8 current_config = 1; + u8 current_interface = 0; + std::array location{}; + +protected: + static u64 get_timestamp(); +}; + +class UsbDevicePassthrough : public UsbDevice { +public: + UsbDevicePassthrough(libusb_device* _device, libusb_device_descriptor& desc, + const std::array& location); + ~UsbDevicePassthrough(); + + int open_device() override; + void read_descriptors() override; + u32 get_configuration(u8* buf) override; + bool set_configuration(u8 cfg_num) override; + bool set_interface(u8 int_num) override; + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, + u32 buf_size, u8* buf, UsbTransfer* transfer) override; + void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; + void isochronous_transfer(UsbTransfer* transfer) override; + +protected: + void send_libusb_transfer(libusb_transfer* transfer); + +protected: + libusb_device* lusb_device = nullptr; + libusb_device_handle* lusb_handle = nullptr; +}; + +class UsbDeviceEmulated : public UsbDevice { +public: + UsbDeviceEmulated(const std::array& location); + UsbDeviceEmulated(const UsbDeviceDescriptor& _device, const std::array& location); + + int open_device() override; + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, + u32 buf_size, u8* buf, UsbTransfer* transfer) override; + void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; + void isochronous_transfer(UsbTransfer* transfer) override; + + // Emulated specific functions + void add_string(std::string str); + u32 get_descriptor(u8 type, u8 index, u8* buf, u32 buf_size); + u32 get_status(bool self_powered, bool remote_wakeup, u8* buf, u32 buf_size); + +protected: + std::vector strings; +}; + +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/usbd.cpp b/src/core/libraries/usbd/usbd.cpp index 10da63b7d6..479833e64c 100644 --- a/src/core/libraries/usbd/usbd.cpp +++ b/src/core/libraries/usbd/usbd.cpp @@ -8,40 +8,12 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "libusb.h" +#include "usb_device.h" #include "usbd.h" +#include "usbd_impl.h" namespace Libraries::Usbd { -libusb_context* ctx = nullptr; - -#if LIBUSB_API_VERSION >= 0x0100010A -static void LIBUSB_CALL printlog_callback(libusb_context* /*ctx*/, enum libusb_log_level level, - const char* str) { - if (!str) { - return; - } - - const std::string str_copy = str; - std::string msg = {}; - const size_t begin = str_copy.find_first_not_of(" \t\n"); - - if (begin != str_copy.npos) { - msg = str_copy.substr(begin, str_copy.find_last_not_of(" \t\n") + 1); - } - switch (level) { - case LIBUSB_LOG_LEVEL_ERROR: - LOG_WARNING(Lib_Usbd, "{}", msg); - break; - case LIBUSB_LOG_LEVEL_INFO: - LOG_INFO(Lib_Usbd, "{}", msg); - case LIBUSB_LOG_LEVEL_DEBUG: - LOG_DEBUG(Lib_Usbd, "{}", msg); - default: - break; - } -} -#endif - static int LibusbErrToOrbis(const int error_code) { ASSERT_MSG(error_code < 1, "Passed an invalid error code!"); @@ -84,19 +56,14 @@ libusb_transfer* PS4_SYSV_ABI sceUsbdAllocTransfer(s32 iso_packets) { } int PS4_SYSV_ABI sceUsbdAttachKernelDriver(libusb_device_handle* dev_handle, s32 interface_num) { - LOG_INFO(Lib_Usbd, "called"); + LOG_ERROR(Lib_Usbd, "(STUBBED)called"); if (dev_handle == nullptr) { return SCE_USBD_ERROR_INVALID_ARG; } - int err = libusb_attach_kernel_driver(dev_handle, interface_num); - return (err == LIBUSB_SUCCESS || err == LIBUSB_ERROR_NOT_SUPPORTED) - ? ORBIS_OK - : SCE_USBD_ERROR_FATAL; // Mercy is for the weak! + return ORBIS_OK; } -int PS4_SYSV_ABI sceUsbdBulkTransfer(libusb_device_handle* dev_handle, unsigned char endpoint, - unsigned char* data, s32 length, s32* transferred, - u32 timeout) { +int PS4_SYSV_ABI sceUsbdBulkTransfer() { LOG_ERROR(Lib_Usbd, "(STUBBED)called"); return ORBIS_OK; } @@ -114,13 +81,16 @@ int PS4_SYSV_ABI sceUsbdCheckConnected(libusb_device_handle* dev_handle) { // Libusb doesn't have this, so I guess we'll check if we can still get the device's info struct libusb_device_descriptor desc; - libusb_device* dev = libusb_get_device(dev_handle); + libusb_device* dev = sceUsbdGetDevice(dev_handle); if (!dev) { - return SCE_USBD_ERROR_INVALID_ARG; //??? + return SCE_USBD_ERROR_NO_DEVICE; //??? } - int ret = libusb_get_device_descriptor(dev, &desc); - return (ret == 0) ? ORBIS_OK : SCE_USBD_ERROR_NO_DEVICE; + int err = sceUsbdGetDeviceDescriptor(dev, &desc); + if (err < 0 || !desc.idProduct) { + return SCE_USBD_ERROR_NO_DEVICE; + } + return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdClaimInterface(libusb_device_handle* dev_handle, s32 interface_num) { @@ -128,6 +98,7 @@ int PS4_SYSV_ABI sceUsbdClaimInterface(libusb_device_handle* dev_handle, s32 int if (dev_handle == nullptr || interface_num < 32) { return SCE_USBD_ERROR_INVALID_ARG; } + UNREACHABLE_MSG("not yet"); int err = libusb_claim_interface(dev_handle, interface_num); return LibusbErrToOrbis(err); } @@ -172,10 +143,7 @@ int PS4_SYSV_ABI sceUsbdDetachKernelDriver(libusb_device_handle* dev_handle, s32 if (dev_handle == nullptr) { return SCE_USBD_ERROR_INVALID_ARG; } - int err = libusb_detach_kernel_driver(dev_handle, interface_num); - return (err == LIBUSB_SUCCESS || err == LIBUSB_ERROR_NOT_SUPPORTED) - ? ORBIS_OK - : SCE_USBD_ERROR_FATAL; // Mercy is for the weak! + return ORBIS_OK; } void PS4_SYSV_ABI sceUsbdEventHandlerActive() { @@ -190,17 +158,13 @@ void PS4_SYSV_ABI sceUsbdEventHandlingOk() { int PS4_SYSV_ABI sceUsbdExit() { LOG_INFO(Lib_Usbd, "called"); - libusb_exit(ctx); + UsbImplementation::Instance()->deinitialize(); return ORBIS_OK; } -void PS4_SYSV_ABI sceUsbdFillBulkTransfer(struct libusb_transfer* transfer, - libusb_device_handle* dev_handle, unsigned char endpoint, - unsigned char* buffer, u32 length, - libusb_transfer_cb_fn callback, void* user_data, - u32 timeout) { +int PS4_SYSV_ABI sceUsbdFillBulkTransfer() { LOG_ERROR(Lib_Usbd, "(STUBBED)called"); - UNREACHABLE_MSG("unimplemented"); + return ORBIS_OK; } void PS4_SYSV_ABI sceUsbdFillControlSetup(unsigned char* buffer, u8 request_type, u8 request, @@ -248,8 +212,8 @@ void PS4_SYSV_ABI sceUsbdFreeDeviceList(libusb_device** list) { } void PS4_SYSV_ABI sceUsbdFreeTransfer(struct libusb_transfer* transfer) { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); - UNREACHABLE_MSG("unimplemented"); + LOG_INFO(Lib_Usbd, "called"); + libusb_free_transfer(transfer); } int PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor(libusb_device* dev, @@ -276,6 +240,7 @@ int PS4_SYSV_ABI sceUsbdGetConfigDescriptor(libusb_device* dev, u8 config_index, if (dev == nullptr || config == nullptr) { return SCE_USBD_ERROR_INVALID_ARG; } + UNREACHABLE_MSG("not yet"); int err = libusb_get_config_descriptor(dev, config_index, config); return LibusbErrToOrbis(err); } @@ -286,6 +251,7 @@ int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue(libusb_device* dev, u8 config if (dev == nullptr || config == nullptr) { return SCE_USBD_ERROR_INVALID_ARG; } + UNREACHABLE_MSG("not yet"); int err = libusb_get_config_descriptor_by_value(dev, config_value, config); return LibusbErrToOrbis(err); } @@ -301,10 +267,12 @@ int PS4_SYSV_ABI sceUsbdGetDescriptor(libusb_device_handle* dev_handle, u8 desc_ return ORBIS_OK; } -libusb_device* PS4_SYSV_ABI sceUsbdGetDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); - UNREACHABLE_MSG("unimplemented"); - return nullptr; +libusb_device* PS4_SYSV_ABI sceUsbdGetDevice(libusb_device_handle* dev_handle) { + LOG_INFO(Lib_Usbd, "called"); + if (dev_handle == nullptr) { + return 0; + } + return libusb_get_device(dev_handle); } int PS4_SYSV_ABI sceUsbdGetDeviceAddress(libusb_device* dev) { @@ -327,7 +295,7 @@ int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor(libusb_device* dev, int PS4_SYSV_ABI sceUsbdGetDeviceList(libusb_device*** list) { LOG_INFO(Lib_Usbd, "called"); - ssize_t count = libusb_get_device_list(ctx, list); + ssize_t count = UsbImplementation::Instance()->get_device_list(list); if (count <= 0) { return LibusbErrToOrbis(count); } @@ -335,8 +303,8 @@ int PS4_SYSV_ABI sceUsbdGetDeviceList(libusb_device*** list) { } int PS4_SYSV_ABI sceUsbdGetDeviceSpeed(libusb_device* dev) { - LOG_ERROR(Lib_Usbd, "(STUBBED)called"); - return ORBIS_OK; + LOG_INFO(Lib_Usbd, "called"); + return libusb_get_device_speed(dev); } unsigned char* PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer(struct libusb_transfer* transfer, @@ -374,44 +342,26 @@ int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii(libusb_device_handle* dev_handl return ORBIS_OK; } -int PS4_SYSV_ABI sceUsbdHandleEvents() { - LOG_INFO(Lib_Usbd, "called"); - int err = libusb_handle_events(ctx); - return LibusbErrToOrbis(err); +int PS4_SYSV_ABI sceUsbdHandleEvents(int* seconds) { + LOG_INFO(Lib_Usbd, "redirecting to HandleEventsTimeout..."); + return sceUsbdHandleEventsTimeout(seconds); } int PS4_SYSV_ABI sceUsbdHandleEventsLocked() { - LOG_INFO(Lib_Usbd, "called"); - int err = libusb_handle_events_locked(ctx, 0); - return LibusbErrToOrbis(err); + LOG_ERROR(Lib_Usbd, "(STUBBED)called"); + return ORBIS_OK; } -int PS4_SYSV_ABI sceUsbdHandleEventsTimeout(int* time_value) { +int PS4_SYSV_ABI sceUsbdHandleEventsTimeout(int* seconds) { LOG_INFO(Lib_Usbd, "called"); - timeval timeout = {*time_value / 1000, (*time_value % 1000) * 1000}; - int err = libusb_handle_events_timeout(ctx, &timeout); + int err = UsbImplementation::Instance()->operate({*seconds, 0}); return LibusbErrToOrbis(err); } int PS4_SYSV_ABI sceUsbdInit() { LOG_INFO(Lib_Usbd, "called"); -#if LIBUSB_API_VERSION >= 0x0100010A - libusb_init_option log_level_opt{}; - log_level_opt.option = LIBUSB_OPTION_LOG_LEVEL; - log_level_opt.value.ival = LIBUSB_LOG_LEVEL_WARNING; - - libusb_init_option log_callback_opt{}; - log_callback_opt.option = LIBUSB_OPTION_LOG_CB; - log_callback_opt.value.log_cbval = &printlog_callback; - - std::vector options = {std::move(log_level_opt), - std::move(log_callback_opt)}; - - int err = libusb_init_context(&ctx, options.data(), static_cast(options.size())); -#else - int err = libusb_init(&ctx); -#endif - return LibusbErrToOrbis(err); + UsbImplementation::Instance()->initialize(); + return ORBIS_OK; } int PS4_SYSV_ABI sceUsbdInterruptTransfer(libusb_device_handle* dev_handle, unsigned char endpoint, @@ -448,14 +398,40 @@ int PS4_SYSV_ABI sceUsbdOpen(libusb_device* dev, libusb_device_handle** dev_hand if (dev == nullptr || dev_handle == nullptr) { return SCE_USBD_ERROR_INVALID_ARG; //??? } - int err = libusb_open(dev, dev_handle); + int err = UsbImplementation::Instance()->open_device(dev, dev_handle); return LibusbErrToOrbis(err); } libusb_device_handle* PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid(u16 vendor_id, u16 product_id) { LOG_INFO(Lib_Usbd, "called"); - auto dev = libusb_open_device_with_vid_pid(ctx, vendor_id, product_id); - return dev; + + struct libusb_device** devs; + struct libusb_device* found = nullptr; + struct libusb_device* dev; + struct libusb_device_handle* dev_handle = nullptr; + size_t i = 0; + + if (sceUsbdGetDeviceList(&devs) < 0) { + return nullptr; + } + + while ((dev = devs[i++]) != nullptr) { + struct libusb_device_descriptor desc; + int r = sceUsbdGetDeviceDescriptor(dev, &desc); + if (r < 0) { + return nullptr; + } + if (desc.idVendor == vendor_id && desc.idProduct == product_id) { + found = dev; + break; + } + } + + if (found) { + return nullptr; + } + + return dev_handle; } void PS4_SYSV_ABI sceUsbdRefDevice() { diff --git a/src/core/libraries/usbd/usbd.h b/src/core/libraries/usbd/usbd.h index 0ae0d98e93..9cabee7efc 100644 --- a/src/core/libraries/usbd/usbd.h +++ b/src/core/libraries/usbd/usbd.h @@ -13,9 +13,7 @@ namespace Libraries::Usbd { libusb_transfer* PS4_SYSV_ABI sceUsbdAllocTransfer(s32 iso_packets); int PS4_SYSV_ABI sceUsbdAttachKernelDriver(libusb_device_handle* dev_handle, s32 interface_num); -int PS4_SYSV_ABI sceUsbdBulkTransfer(libusb_device_handle* dev_handle, unsigned char endpoint, - unsigned char* data, s32 length, s32* transferred, - u32 timeout); +int PS4_SYSV_ABI sceUsbdBulkTransfer(); int PS4_SYSV_ABI sceUsbdCancelTransfer(struct libusb_transfer* transfer); int PS4_SYSV_ABI sceUsbdCheckConnected(libusb_device_handle* dev_handle); int PS4_SYSV_ABI sceUsbdClaimInterface(libusb_device_handle* dev_handle, s32 interface_num); @@ -31,11 +29,7 @@ int PS4_SYSV_ABI sceUsbdDetachKernelDriver(libusb_device_handle* dev_handle, s32 void PS4_SYSV_ABI sceUsbdEventHandlerActive(); void PS4_SYSV_ABI sceUsbdEventHandlingOk(); int PS4_SYSV_ABI sceUsbdExit(); -void PS4_SYSV_ABI sceUsbdFillBulkTransfer(struct libusb_transfer* transfer, - libusb_device_handle* dev_handle, unsigned char endpoint, - unsigned char* buffer, u32 length, - libusb_transfer_cb_fn callback, void* user_data, - u32 timeout); +int PS4_SYSV_ABI sceUsbdFillBulkTransfer(); void PS4_SYSV_ABI sceUsbdFillControlSetup(unsigned char* buffer, u8 request_type, u8 request, u16 value, u16 index, u16 length); void PS4_SYSV_ABI sceUsbdFillControlTransfer(struct libusb_transfer* transfer, @@ -65,7 +59,7 @@ int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue(libusb_device* dev, u8 config int PS4_SYSV_ABI sceUsbdGetConfiguration(libusb_device_handle* dev_handle, s32* config); int PS4_SYSV_ABI sceUsbdGetDescriptor(libusb_device_handle* dev_handle, u8 desc_type, u8 desc_index, unsigned char* data, s32 length); -libusb_device* PS4_SYSV_ABI sceUsbdGetDevice(); +libusb_device* PS4_SYSV_ABI sceUsbdGetDevice(libusb_device_handle* dev_handle); int PS4_SYSV_ABI sceUsbdGetDeviceAddress(libusb_device* dev); int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor(libusb_device* dev, struct libusb_device_descriptor* config); @@ -78,7 +72,7 @@ int PS4_SYSV_ABI sceUsbdGetStringDescriptor(libusb_device_handle* dev_handle, u8 u16 lang_id, unsigned char* data, s32 length); int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii(libusb_device_handle* dev_handle, u8 desc_index, u16 lang_id, unsigned char* data, s32 length); -int PS4_SYSV_ABI sceUsbdHandleEvents(); +int PS4_SYSV_ABI sceUsbdHandleEvents(int* seconds); int PS4_SYSV_ABI sceUsbdHandleEventsLocked(); int PS4_SYSV_ABI sceUsbdHandleEventsTimeout(int* time_value); int PS4_SYSV_ABI sceUsbdInit(); diff --git a/src/core/libraries/usbd/usbd_impl.cpp b/src/core/libraries/usbd/usbd_impl.cpp new file mode 100644 index 0000000000..3d808a6f8c --- /dev/null +++ b/src/core/libraries/usbd/usbd_impl.cpp @@ -0,0 +1,499 @@ +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/types.h" +#include "fmt/std.h" +#include "libusb.h" +#include "libusbi.h" +#include "usbd_impl.h" + +namespace Libraries::Usbd { + +void LIBUSB_CALL UsbHandler::callback_transfer(struct libusb_transfer* transfer) { + LOG_INFO(Lib_Usbd, "Called"); + auto usbh = UsbImplementation::Instance(); + + if (!usbh->is_init) + return; + + usbh->transfer_complete(transfer); +} + +#if LIBUSB_API_VERSION >= 0x0100010A +static void LIBUSB_CALL printlog_callback(libusb_context* /*ctx*/, enum libusb_log_level level, + const char* str) { + if (!str) { + return; + } + + const std::string str_copy = str; + std::string msg = {}; + const size_t begin = str_copy.find_first_not_of(" \t\n"); + + if (begin != str_copy.npos) { + msg = str_copy.substr(begin, str_copy.find_last_not_of(" \t\n") + 1); + } + switch (level) { + case LIBUSB_LOG_LEVEL_ERROR: + LOG_WARNING(Lib_Usbd, "{}", msg); + break; + case LIBUSB_LOG_LEVEL_INFO: + LOG_INFO(Lib_Usbd, "{}", msg); + case LIBUSB_LOG_LEVEL_DEBUG: + LOG_DEBUG(Lib_Usbd, "{}", msg); + default: + break; + } +} +#endif + +UsbHandler::UsbHandler() { + // initialize(); +} + +void UsbHandler::initialize() { +#if LIBUSB_API_VERSION >= 0x0100010A + libusb_init_option log_lv_opt{}; + log_lv_opt.option = LIBUSB_OPTION_LOG_LEVEL; + log_lv_opt.value.ival = + LIBUSB_LOG_LEVEL_WARNING; // You can also set the LIBUSB_DEBUG env variable instead + + libusb_init_option log_cb_opt{}; + log_cb_opt.option = LIBUSB_OPTION_LOG_CB; + log_cb_opt.value.log_cbval = &printlog_callback; + + std::vector options = {std::move(log_lv_opt), std::move(log_cb_opt)}; + + if (int res = libusb_init_context(&ctx, options.data(), static_cast(options.size())); + res < 0) +#else + if (int res = libusb_init(&ctx); res < 0) +#endif + { + LOG_ERROR(Lib_Usbd, "Failed to initialize: {}", libusb_error_name(res)); + return; + } + + for (u32 index = 0; index < MAX_SYS_USBD_TRANSFERS; index++) { + transfers[index].transfer = libusb_alloc_transfer(8); + transfers[index].transfer_id = index; + } + + // look if any device which we could be interested in is actually connected + libusb_device** list = nullptr; + ssize_t ndev = libusb_get_device_list(ctx, &list); + + if (ndev < 0) { + LOG_ERROR(Lib_Usbd, "Failed to get device list: %s", + libusb_error_name(static_cast(ndev))); + return; + } + + bool found_skylander = false; + bool found_infinity = false; + bool found_dimension = false; + + for (ssize_t index = 0; index < ndev; index++) { + libusb_device_descriptor desc{}; + if (int res = libusb_get_device_descriptor(list[index], &desc); res < 0) { + LOG_ERROR(Lib_Usbd, "Failed to get device descriptor: %s", libusb_error_name(res)); + continue; + } + + auto check_device = [&](const u16 id_vendor, const u16 id_product, + const char* s_name) -> bool { + if (desc.idVendor == id_vendor && desc.idProduct == id_product && true) { + LOG_INFO(Lib_Usbd, "Found device: %s", s_name); + libusb_ref_device(list[index]); + std::shared_ptr usb_dev = + std::make_shared(list[index], desc, get_new_location()); + usb_devices.push_back(usb_dev); + return true; + } + return false; + }; + + found_skylander = check_device(0x1430, 0x0150, "Skylanders Portal"); + found_infinity = check_device(0x0E6F, 0x0129, "Disney Infinity Base"); + found_dimension = check_device(0x0E6F, 0x0241, "Lego Dimensions Portal"); + } + + libusb_free_device_list(list, 1); + + /*if (!found_skylander) { + LOG_INFO(Lib_Usbd, "Adding emulated skylander"); + usb_devices.push_back(std::make_shared(get_new_location())); + } + + if (!found_infinity) { + LOG_INFO(Lib_Usbd, "Adding emulated infinity base"); + usb_devices.push_back(std::make_shared(get_new_location())); + } + + if (!found_dimension) { + LOG_INFO(Lib_Usbd, "Adding emulated dimension toypad"); + usb_devices.push_back(std::make_shared(get_new_location())); + }*/ +} + +UsbHandler::~UsbHandler() { + // deinitialize(); +} + +void UsbHandler::deinitialize() { + // Ensures shared_ptr are all cleared before terminating libusb + open_devices.clear(); + usb_devices.clear(); + + for (u32 index = 0; index < MAX_SYS_USBD_TRANSFERS; index++) { + if (transfers[index].transfer) + libusb_free_transfer(transfers[index].transfer); + } + + if (ctx) + libusb_exit(ctx); +} + +int UsbHandler::operate(timeval lusb_tv) { + if (ctx) { + // Todo: Hotplug here? + + // Process asynchronous requests that are pending + libusb_handle_events_timeout_completed(ctx, &lusb_tv, nullptr); + + // Process fake transfers + if (!fake_transfers.empty()) { + std::lock_guard lock_tf(mutex_transfers); + u64 timestamp = 0 /*get_system_time() - Emu.GetPauseTime()*/; + + for (auto it = fake_transfers.begin(); it != fake_transfers.end();) { + auto transfer = *it; + + ASSERT(transfer->busy && transfer->fake); + + if (transfer->expected_time > timestamp) { + ++it; + continue; + } + + transfer->result = transfer->expected_result; + transfer->count = transfer->expected_count; + transfer->fake = false; + transfer->busy = false; + + it = fake_transfers.erase(it); // if we've processed this, then we erase this entry + // (replacing the iterator with the new reference) + } + } + + //// If there is no handled devices usb thread is not actively needed + // if (handled_devices.empty()) + // thread_ctrl::wait_for(500'000); + // else + // thread_ctrl::wait_for(1'000); + } + return 0; +} + +void UsbHandler::send_message(u32 message, u32 tr_id) { + add_event(message, tr_id, 0x00); +} + +void UsbHandler::transfer_complete(struct libusb_transfer* transfer) { + std::lock_guard lock_tf(mutex_transfers); + + UsbTransfer* usbd_transfer = static_cast(transfer->user_data); + + if (transfer->status != 0) { + LOG_ERROR(Lib_Usbd, "Transfer Error: %d", +transfer->status); + } + + usbd_transfer->result = transfer->status; + + if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { + for (const auto& dev : usb_devices) { + // if (dev->assigned_number == usbd_transfer->assigned_number) { + disconnect_usb_device(dev, true); + // break; + //} + } + } + + usbd_transfer->count = transfer->actual_length; + + for (s32 index = 0; index < transfer->num_iso_packets; index++) { + u8 iso_status = transfer->iso_packet_desc[index].status; + usbd_transfer->iso_request.packets[index] = + ((iso_status & 0xF) << 12 | (transfer->iso_packet_desc[index].actual_length & 0xFFF)); + } + + if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL && usbd_transfer->control_destbuf) { + memcpy(usbd_transfer->control_destbuf, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, + transfer->actual_length); + usbd_transfer->control_destbuf = nullptr; + } + + usbd_transfer->busy = false; + + LOG_TRACE(Lib_Usbd, "Transfer complete(0x%x): %s", usbd_transfer->transfer_id, *transfer); +} + +bool UsbHandler::get_event(u64* arg1, u64* arg2, u64* arg3) { + if (!usbd_events.empty()) { + const auto& usb_event = usbd_events.front(); + *arg1 = std::get<0>(usb_event); + *arg2 = std::get<1>(usb_event); + *arg3 = std::get<2>(usb_event); + usbd_events.pop(); + LOG_TRACE(Lib_Usbd, "Received event: arg1=0x%x arg2=0x%x arg3=0x%x", *arg1, *arg2, *arg3); + return true; + } + + return false; +} + +void UsbHandler::add_event(u64 arg1, u64 arg2, u64 arg3) { + std::lock_guard lock_sq(mutex_sq); + + LOG_TRACE(Lib_Usbd, "Sending event: arg1=0x%x arg2=0x%x arg3=0x%x", arg1, arg2, arg3); + usbd_events.emplace(arg1, arg2, arg3); +} + +u32 UsbHandler::get_free_transfer_id() { + u32 num_loops = 0; + do { + num_loops++; + transfer_counter++; + + if (transfer_counter >= MAX_SYS_USBD_TRANSFERS) { + transfer_counter = 0; + } + + if (num_loops > MAX_SYS_USBD_TRANSFERS) { + LOG_ERROR(Lib_Usbd, "Usb transfers are saturated!"); + } + } while (transfers[transfer_counter].busy); + + return transfer_counter; +} + +UsbTransfer& UsbHandler::get_transfer(u32 transfer_id) { + return transfers[transfer_id]; +} + +std::pair UsbHandler::get_free_transfer() { + std::lock_guard lock_tf(mutex_transfers); + + u32 transfer_id = get_free_transfer_id(); + auto& transfer = get_transfer(transfer_id); + transfer.busy = true; + + return {transfer_id, transfer}; +} + +std::pair UsbHandler::get_transfer_status(u32 transfer_id) { + std::lock_guard lock_tf(mutex_transfers); + + const auto& transfer = get_transfer(transfer_id); + + return {transfer.result, transfer.count}; +} + +std::pair UsbHandler::get_isochronous_transfer_status( + u32 transfer_id) { + std::lock_guard lock_tf(mutex_transfers); + + const auto& transfer = get_transfer(transfer_id); + + return {transfer.result, transfer.iso_request}; +} + +void UsbHandler::push_fake_transfer(UsbTransfer* transfer) { + std::lock_guard lock_tf(mutex_transfers); + fake_transfers.push_back(transfer); +} + +const std::array& UsbHandler::get_new_location() { + location[0]++; + return location; +} + +int UsbHandler::open_usb_device(std::shared_ptr dev) { + size_t size = sizeof(libusb_device_handle) + usbi_backend.device_handle_priv_size; + void* aligned_new = ::operator new(size, std::align_val_t(sizeof(void*))); + + int err = dev->open_device(); + if (err != LIBUSB_SUCCESS) { + LOG_ERROR(Lib_Usbd, "Failed to open USB device(VID=0x%04x, PID=0x%04x)", + dev->device._device.idVendor, dev->device._device.idProduct); + return err; + } + + open_devices.push_back(dev); + LOG_INFO(Lib_Usbd, "USB device(VID=0x%04x, PID=0x%04x)", dev->device._device.idVendor, + dev->device._device.idProduct); + return err; +} + +void UsbHandler::disconnect_usb_device(std::shared_ptr dev, + bool update_usb_devices) { + auto it = find(open_devices.begin(), open_devices.end(), dev); + if (it != open_devices.end()) { + open_devices.erase(it); + LOG_INFO(Lib_Usbd, "USB device(VID={}, PID={}) unassigned", dev->device._device.idVendor, + dev->device._device.idProduct); + } + + if (update_usb_devices) { + auto it = find(usb_devices.begin(), usb_devices.end(), dev); + if (it != usb_devices.end()) { + usb_devices.erase(it); + } + } +} + +ssize_t UsbHandler::get_device_list(libusb_device*** list) { + // discovered_devs_alloc() + auto discdevs = std::unique_ptr(new DiscoveredDevices{0, 16, {}}); + discdevs->devices.reserve(16); + + size_t size = sizeof(libusb_device) + usbi_backend.device_priv_size; + void* aligned_new = ::operator new(size, std::align_val_t(sizeof(void*))); + + for (int i = 0; i < usb_devices.size(); i++) { + auto session_id = next_session_id; + next_session_id++; + + // usbi_alloc_device() + libusb_device* dev = new (aligned_new) libusb_device; + + usbi_atomic_store(&dev->refcnt, 1); + dev->session_data = session_id; + dev->speed = LIBUSB_SPEED_UNKNOWN; + + // We don't have real buses in WebUSB, just pretend everything + // is on bus 1. + dev->bus_number = i + 1; + // This can wrap around but it's the best approximation of a stable + // device address and port number we can provide. + dev->device_address = dev->port_number = (u8)session_id; + + auto usb_dev = usb_devices[i].get(); + usb_dev->read_descriptors(); + + UsbDeviceDescriptor usb_dev_desc = usb_dev->device._device; + + dev->device_descriptor.bcdDevice = usb_dev_desc.bcdDevice; + dev->device_descriptor.bcdUSB = usb_dev_desc.bcdUSB; + dev->device_descriptor.bDescriptorType = LIBUSB_DT_DEVICE; + dev->device_descriptor.bDeviceClass = usb_dev_desc.bDeviceClass; + dev->device_descriptor.bDeviceProtocol = usb_dev_desc.bDeviceProtocol; + dev->device_descriptor.bDeviceSubClass = usb_dev_desc.bDeviceSubClass; + dev->device_descriptor.bLength = LIBUSB_DT_DEVICE_SIZE; + dev->device_descriptor.bMaxPacketSize0 = usb_dev_desc.bMaxPacketSize0; + dev->device_descriptor.bNumConfigurations = usb_dev_desc.bNumConfigurations; + dev->device_descriptor.idProduct = usb_dev_desc.idProduct; + dev->device_descriptor.idVendor = usb_dev_desc.idVendor; + dev->device_descriptor.iManufacturer = usb_dev_desc.iManufacturer; + dev->device_descriptor.iProduct = usb_dev_desc.iProduct; + dev->device_descriptor.iSerialNumber = usb_dev_desc.iSerialNumber; + + dev->attached = 1; + + // Infer the device speed from the descriptor. + if (usb_dev_desc.bMaxPacketSize0 == 9) { + dev->speed = dev->device_descriptor.bcdUSB >= 0x0310 ? LIBUSB_SPEED_SUPER_PLUS + : LIBUSB_SPEED_SUPER; + } else if (dev->device_descriptor.bcdUSB >= 0x0200) { + dev->speed = LIBUSB_SPEED_HIGH; + } else if (dev->device_descriptor.bMaxPacketSize0 > 8) { + dev->speed = LIBUSB_SPEED_FULL; + } else { + dev->speed = LIBUSB_SPEED_LOW; + } + // discovered_devs_append() + if (discdevs->length > discdevs->capacity) { + discdevs->capacity += 16; + discdevs->devices.reserve(discdevs->capacity); + } + discdevs->devices.push_back(libusb_ref_device(dev)); + discdevs->length++; + libusb_unref_device(dev); + } + + ssize_t count = static_cast(discdevs->length); + + auto list_copy = new libusb_device*[count + 1]; // +1 for the null terminator + if (!list_copy) { + count = LIBUSB_ERROR_NO_MEM; + goto out; + } + + for (ssize_t i = 0; i < count; ++i) { + libusb_device* dev = discdevs->devices[i]; + list_copy[i] = libusb_ref_device(dev); + } + list_copy[count] = nullptr; + *list = list_copy; + +out: + // discovered_devs_free() + for (size_t i = 0; i < discdevs->length; ++i) { + libusb_unref_device(discdevs->devices[i]); + } + // discdevs frees itself + return count; +} + +int UsbHandler::open_device(libusb_device* dev, libusb_device_handle** dev_handle) { + struct libusb_device_handle* _dev_handle; + + auto* dev2 = + UsbImplementation::Instance()->get_device_from_bus_number(libusb_get_bus_number(dev)); + if (dev2 == nullptr) { + return LIBUSB_ERROR_NO_DEVICE; + } + + size_t size = sizeof(*_dev_handle) + usbi_backend.device_handle_priv_size; + void* aligned_new = ::operator new(size, std::align_val_t(sizeof(void*))); + + _dev_handle = new (aligned_new) libusb_device_handle; + if (!_dev_handle) { + return LIBUSB_ERROR_NO_MEM; + } + + _dev_handle->dev = libusb_ref_device(dev); + + int err = UsbImplementation::Instance()->open_usb_device(*dev2); + if (err < 0) { + libusb_unref_device(dev); + free(_dev_handle); + return err; + } + + *dev_handle = _dev_handle; + + return 0; +} + +std::shared_ptr* UsbHandler::get_device_from_ids(u16 vendor_id, u16 product_id) { + auto it = std::find_if(usb_devices.begin(), usb_devices.end(), + [vendor_id, product_id](const std::shared_ptr& dev) { + return dev && dev->device._device.idVendor == vendor_id && + dev->device._device.idProduct == product_id; + }); + return (it != usb_devices.end()) ? &(*it) : nullptr; +} + +std::shared_ptr* UsbHandler::get_device_from_bus_number(int bus_number) { + if (bus_number < 0 || bus_number >= usb_devices.size()) { + return nullptr; + } + return &usb_devices[bus_number]; +} +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/usbd_impl.h b/src/core/libraries/usbd/usbd_impl.h new file mode 100644 index 0000000000..18632355b6 --- /dev/null +++ b/src/core/libraries/usbd/usbd_impl.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "common/singleton.h" +#include "common/types.h" +#include "libusb.h" +#include "usb_device.h" + +namespace Libraries::Usbd { + +#define MAX_SYS_USBD_TRANSFERS 0x44 + +struct UsbStringHash { + using hash_type = std::hash; + using is_transparent = void; + + std::size_t operator()(const char* str) const { + return hash_type{}(str); + } + std::size_t operator()(std::string_view str) const { + return hash_type{}(str); + } + std::size_t operator()(std::string const& str) const { + return hash_type{}(str); + } +}; + +struct UsbPipe { + std::shared_ptr device = nullptr; + + u8 endpoint = 0; +}; + +struct DiscoveredDevices { + size_t length; + size_t capacity; + std::vector devices; +}; + +class UsbHandler { +public: + void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer); + + UsbHandler(); + void initialize(); + ~UsbHandler(); + void deinitialize(); + + int operate(timeval lusb_tv); + + void transfer_complete(libusb_transfer* transfer); + + bool get_event(u64* arg1, u64* arg2, u64* arg3); + void add_event(u64 arg1, u64 arg2, u64 arg3); + + std::pair get_free_transfer(); + std::pair get_transfer_status(u32 transfer_id); + std::pair get_isochronous_transfer_status(u32 transfer_id); + void push_fake_transfer(UsbTransfer* transfer); + + const std::array& get_new_location(); + int open_usb_device(std::shared_ptr dev); + void disconnect_usb_device(std::shared_ptr dev, bool update_usb_devices); + + unsigned long next_session_id = 0; + ssize_t get_device_list(libusb_device*** list); + + int open_device(libusb_device* dev, libusb_device_handle** dev_handle); + + std::shared_ptr* get_device_from_ids(u16 vendor_id, u16 product_id); + std::shared_ptr* get_device_from_bus_number(int bus_number); + + std::vector> open_devices; + + std::shared_mutex mutex; + std::atomic is_init = false; + + // sys_usbd_receive_event PPU Threads + std::shared_mutex mutex_sq; + + static constexpr auto thread_name = "Usb Manager Thread"; + +private: + // Lock free functions for internal use(ie make sure to lock before using those) + UsbTransfer& get_transfer(u32 transfer_id); + u32 get_free_transfer_id(); + + void send_message(u32 message, u32 tr_id); + +private: + // Counters for device IDs, transfer IDs and pipe IDs + std::atomic dev_counter = 1; + u32 transfer_counter = 0; + + // Transfers infos + std::shared_mutex mutex_transfers; + std::array transfers; + std::vector fake_transfers; + + // Queue of pending usbd events + std::queue> usbd_events; + + // List of devices "connected" to the ps3 + std::array location{}; + std::vector> usb_devices; + + libusb_context* ctx = nullptr; +}; + +using UsbImplementation = Common::Singleton; +} // namespace Libraries::Usbd \ No newline at end of file