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
This commit is contained in:
Luca Silva 2026-04-27 22:43:21 +02:00 committed by Megamouse
parent 771837ee55
commit 6d058586c4

View file

@ -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<u8>(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);