From e0fe7989e91f89ee312a1738d9be700c7e3040ed Mon Sep 17 00:00:00 2001 From: brian218 Date: Wed, 18 Jan 2023 10:21:55 +0800 Subject: [PATCH] USB: Added VFS USB mass storage devices to the USB device list --- rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/Cell/lv2/sys_usbd.cpp | 31 ++++++++ rpcs3/Emu/Io/usb_device.cpp | 122 +++++++++++++++++++++++++------- rpcs3/Emu/Io/usb_device.h | 5 +- rpcs3/Emu/Io/usb_vfs.cpp | 62 ++++++++++++++++ rpcs3/Emu/Io/usb_vfs.h | 10 +++ rpcs3/emucore.vcxproj | 2 + rpcs3/emucore.vcxproj.filters | 6 ++ 8 files changed, 213 insertions(+), 26 deletions(-) create mode 100644 rpcs3/Emu/Io/usb_vfs.cpp create mode 100644 rpcs3/Emu/Io/usb_vfs.h diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index cc17f58a70..a77bd9d0ab 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -389,6 +389,7 @@ target_sources(rpcs3_emu PRIVATE Io/pad_config_types.cpp Io/PadHandler.cpp Io/usb_device.cpp + Io/usb_vfs.cpp Io/Skylander.cpp Io/GHLtar.cpp Io/Buzz.cpp diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp index 3de3b6f43a..d18635bbdc 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp @@ -3,15 +3,18 @@ #include "sys_ppu_thread.h" #include "sys_sync.h" +#include #include #include "Emu/System.h" #include "Emu/Memory/vm.h" #include "Emu/IdManager.h" +#include "Emu/vfs_config.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Io/usb_device.h" +#include "Emu/Io/usb_vfs.h" #include "Emu/Io/Skylander.h" #include "Emu/Io/GHLtar.h" #include "Emu/Io/Buzz.h" @@ -290,6 +293,34 @@ usb_handler_thread::usb_handler_thread() libusb_free_device_list(list, 1); + for (int i = 0; i < 8; i++) // Add VFS USB mass storage devices (/dev_usbXXX) to the USB device list + { + const cfg::device_info device = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, fmt::format("/dev_usb%03d", i)); + + if (device.path.empty() || device.vid.empty() || device.pid.empty()) + continue; + + u16 vid{}; + { + auto [ptr, err] = std::from_chars(device.vid.data(), device.vid.data() + device.vid.size(), vid, 16); + if (err != std::errc()) + { + fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message()); + } + } + + u16 pid{}; + { + auto [ptr, err] = std::from_chars(device.pid.data(), device.pid.data() + device.pid.size(), pid, 16); + if (err != std::errc()) + { + fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message()); + } + } + + usb_devices.push_back(std::make_shared(get_new_location(), vid, pid, device.serial)); + } + if (!found_skylander) { sys_usbd.notice("Adding emulated skylander"); diff --git a/rpcs3/Emu/Io/usb_device.cpp b/rpcs3/Emu/Io/usb_device.cpp index 17d60c2974..8a324494d9 100644 --- a/rpcs3/Emu/Io/usb_device.cpp +++ b/rpcs3/Emu/Io/usb_device.cpp @@ -27,6 +27,12 @@ void usb_device::read_descriptors() { } +u32 usb_device::get_configuration(u8* buf) +{ + *buf = current_config; + return sizeof(u8); +} + bool usb_device::set_configuration(u8 cfg_num) { current_config = cfg_num; @@ -123,6 +129,11 @@ void usb_device_passthrough::read_descriptors() } } +u32 usb_device_passthrough::get_configuration(u8* buf) +{ + return (libusb_get_configuration(lusb_handle, reinterpret_cast(buf)) == LIBUSB_SUCCESS) ? sizeof(u8) : 0; +}; + bool usb_device_passthrough::set_configuration(u8 cfg_num) { usb_device::set_configuration(cfg_num); @@ -187,32 +198,85 @@ bool usb_device_emulated::open_device() return true; } -s32 usb_device_emulated::get_descriptor(u8 type, u8 index, u8* ptr, u32 /*max_size*/) +u32 usb_device_emulated::get_descriptor(u8 type, u8 index, u8* buf, u32 buf_size) { - if (type == USB_DESCRIPTOR_STRING) + u32 expected_count = 2; + + if (buf_size < expected_count) { - if (index < strings.size()) - { - u8 string_len = ::narrow(strings[index].size()); - ptr[0] = (string_len * 2) + 2; - ptr[1] = USB_DESCRIPTOR_STRING; - for (u32 i = 0; i < string_len; i++) - { - ptr[2 + (i * 2)] = strings[index].data()[i]; - ptr[3 + (i * 2)] = 0; - } - return ptr[0]; - } - } - else - { - sys_usbd.error("[Emulated]: Trying to get a descriptor other than string descriptor"); + sys_usbd.error("Illegal buf_size: get_descriptor(type=0x%02x, index=0x%02x, buf=*0x%x, buf_size=0x%x)", type, index, buf, buf_size); + return 0; } - return -1; + buf[0] = expected_count; + buf[1] = type; + + switch (type) + { + case USB_DESCRIPTOR_DEVICE: + { + buf[0] = device.bLength; + expected_count = std::min(device.bLength, ::narrow(buf_size)); + memcpy(buf + 2, device.data, expected_count - 2); + break; + } + case USB_DESCRIPTOR_CONFIG: + { + if (index < device.subnodes.size()) + { + buf[0] = device.subnodes[index].bLength; + expected_count = std::min(device.subnodes[index].bLength, ::narrow(buf_size)); + memcpy(buf + 2, device.subnodes[index].data, expected_count - 2); + } + break; + } + case USB_DESCRIPTOR_STRING: + { + if (index < strings.size() + 1) + { + if (index == 0) + { + constexpr u8 len = sizeof(u16) + 2; + buf[0] = len; + expected_count = std::min(len, ::narrow(buf_size)); + constexpr le_t langid = 0x0409; // English (United States) + memcpy(buf + 2, &langid, expected_count - 2); + } + else + { + const u8 len = std::min(strings[index - 1].size() * 2 + 2, static_cast(0xFF)); + buf[0] = len; + expected_count = std::min(len, ::narrow(buf_size)); + for (u32 i = 0; i < expected_count - 2; i++) + { + buf[i + 2] = i % 2 == 0 ? strings[index - 1].data()[i / 2] : 0; + } + } + } + break; + } + default: sys_usbd.error("Unhandled DescriptorType: get_descriptor(type=0x%02x, index=0x%02x, buf=*0x%x, buf_size=0x%x)", type, index, buf, buf_size); break; + } + + return expected_count; } -void usb_device_emulated::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 /*wIndex*/, u16 /*wLength*/, u32 buf_size, u8* /*buf*/, UsbTransfer* transfer) +u32 usb_device_emulated::get_status(bool self_powered, bool remote_wakeup, u8* buf, u32 buf_size) +{ + constexpr u32 expected_count = sizeof(u16); + + if (buf_size < expected_count) + { + sys_usbd.error("Illegal buf_size: get_status(self_powered=0x%02x, remote_wakeup=0x%02x, buf=*0x%x, buf_size=0x%x)", self_powered, remote_wakeup, buf, buf_size); + return 0; + } + + const u16 device_status = self_powered | remote_wakeup << 1; + memcpy(buf, &device_status, expected_count); + return expected_count; +} + +void usb_device_emulated::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; @@ -221,15 +285,23 @@ void usb_device_emulated::control_transfer(u8 bmRequestType, u8 bRequest, u16 wV switch (bmRequestType) { - case 0: + case LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE: switch (bRequest) { - case 0x09: usb_device::set_configuration(::narrow(wValue)); break; - default: sys_usbd.fatal("Unhandled control transfer(0): 0x%x", bRequest); break; + case LIBUSB_REQUEST_SET_CONFIGURATION: usb_device::set_configuration(::narrow(wValue)); break; + default: sys_usbd.error("Unhandled control transfer(0x%02x): 0x%02x", bmRequestType, bRequest); break; } break; - case 0x80: sys_usbd.todo("Unimplemented control transfer: 0x%x", bmRequestType); break; - default: sys_usbd.fatal("Unhandled control transfer: 0x%x", bmRequestType); break; + case LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE: + 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: sys_usbd.error("Unhandled control transfer(0x%02x): 0x%02x", bmRequestType, bRequest); break; + } + break; + default: sys_usbd.error("Unhandled control transfer: 0x%02x", bmRequestType); break; } } diff --git a/rpcs3/Emu/Io/usb_device.h b/rpcs3/Emu/Io/usb_device.h index 3220d1c4c2..36d6aeb580 100644 --- a/rpcs3/Emu/Io/usb_device.h +++ b/rpcs3/Emu/Io/usb_device.h @@ -177,6 +177,7 @@ public: 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); @@ -207,6 +208,7 @@ public: bool 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; @@ -234,7 +236,8 @@ public: // Emulated specific functions void add_string(char* str); - s32 get_descriptor(u8 type, u8 index, u8* ptr, u32 max_size); + 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; diff --git a/rpcs3/Emu/Io/usb_vfs.cpp b/rpcs3/Emu/Io/usb_vfs.cpp new file mode 100644 index 0000000000..c0a7f5c38f --- /dev/null +++ b/rpcs3/Emu/Io/usb_vfs.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "usb_vfs.h" + +LOG_CHANNEL(usb_vfs); + +usb_device_vfs::usb_device_vfs(const std::array& location, const u16 vid, const u16 pid, const std::string& serial) + : usb_device_emulated(location) +{ + device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, + UsbDeviceDescriptor{ + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x40, + .idVendor = vid, + .idProduct = pid, + .bcdDevice = pid, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01}); + + auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, + UsbDeviceConfiguration{ + .wTotalLength = 0x0020, + .bNumInterfaces = 0x01, + .bConfigurationValue = 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0x80, + .bMaxPower = 0x32})); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface{ + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = 0x08, + .bInterfaceSubClass = 0x06, + .bInterfaceProtocol = 0x50, + .iInterface = 0x00})); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, + UsbDeviceEndpoint{ + .bEndpointAddress = 0x81, + .bmAttributes = 0x02, + .wMaxPacketSize = 0x0200, + .bInterval = 0xFF})); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, + UsbDeviceEndpoint{ + .bEndpointAddress = 0x02, + .bmAttributes = 0x02, + .wMaxPacketSize = 0x0200, + .bInterval = 0xFF})); + + strings = {"SMI Corporation", "USB DISK", serial}; // Manufacturer, Product, SerialNumber +} + +usb_device_vfs::~usb_device_vfs() +{ +} diff --git a/rpcs3/Emu/Io/usb_vfs.h b/rpcs3/Emu/Io/usb_vfs.h new file mode 100644 index 0000000000..a5c9faa517 --- /dev/null +++ b/rpcs3/Emu/Io/usb_vfs.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Emu/Io/usb_device.h" + +class usb_device_vfs : public usb_device_emulated +{ +public: + usb_device_vfs(const std::array& location, const u16 vid, const u16 pid, const std::string& serial); + ~usb_device_vfs(); +}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 4bcc6ab645..cf1561e5c1 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -370,6 +370,7 @@ + @@ -641,6 +642,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 900040a7b8..1d2fe9dda1 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -870,6 +870,9 @@ Emu\Io + + Emu\Io + Utilities @@ -1851,6 +1854,9 @@ Emu\Io + + Emu\Io + Crypto