From 6d058586c460c95fc2fb71df518621df05ca9ee1 Mon Sep 17 00:00:00 2001 From: Luca Silva Date: Mon, 27 Apr 2026 22:43:21 +0200 Subject: [PATCH] USB: remove zero-length IN URB fake-completion workaround The passthrough path was fake-completing zero-length bulk/interrupt IN URBs to mirror the emulated path, so games that drain-poll between transfers wouldn't stall the libusb worker thread. This baked an emulator-side assumption about device behaviour into the host stack. Real PS3 USIO devices issue a ZLP on the read endpoint after any transfer that would otherwise leave the host's drain URB outstanding (notably after a CMD_WRITE). Devices that follow the same protocol (e.g. ITAIKO firmware) are expected to do the same; faking it here masks firmware bugs and diverges from real hardware behaviour. Drop the workaround and let libusb submit the URB normally. The IN URB now completes when the device sends its ZLP, matching real PS3. Refs: https://github.com/RPCS3/rpcs3/pull/18636#discussion_r3143290987 --- rpcs3/Emu/Io/usb_device.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Io/usb_device.cpp b/rpcs3/Emu/Io/usb_device.cpp index 8c28517b94..a456866e0b 100644 --- a/rpcs3/Emu/Io/usb_device.cpp +++ b/rpcs3/Emu/Io/usb_device.cpp @@ -188,24 +188,15 @@ void usb_device_passthrough::control_transfer(u8 bmRequestType, u8 bRequest, u16 void usb_device_passthrough::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) { - // Zero-length bulk/interrupt IN URBs hang in libusb until the device sends a ZLP. - // The emulated path fake-completes these immediately with count=0; mirror that here - // so games that do drain-polls between transfers don't stall the worker thread. - if (buf_size == 0 && (endpoint & LIBUSB_ENDPOINT_IN)) - { - transfer->fake = true; - transfer->expected_count = 0; - transfer->expected_result = HC_CC_NOERR; - transfer->expected_time = get_timestamp() + 1'000; - return; - } - // Pick the libusb helper matching the endpoint's actual transfer type. The PS3 USB // stack routes both bulk and interrupt transfers through this method, but submitting // an interrupt URB to a bulk endpoint fails with EINVAL on Linux. const UsbDeviceEndpoint* ep_desc = find_endpoint(static_cast(endpoint)); const bool is_bulk = ep_desc && (ep_desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK; + sys_usbd.notice("USIO debug: submitting passthrough transfer endpoint=0x%x dir=%s size=0x%x type=%s", + endpoint, (endpoint & LIBUSB_ENDPOINT_IN) ? "IN" : "OUT", buf_size, is_bulk ? "bulk" : "interrupt"); + if (is_bulk) { libusb_fill_bulk_transfer(transfer->transfer, lusb_handle, endpoint, buf, buf_size, callback_transfer, transfer, 0);