Skip to content

Conversation

@loicpoulain
Copy link

The previous assumption that every other transfer in Firehose reads is null was incorrect. A Zero-Length Packet (ZLP) is only sent when the received chunk size exactly matches the USB IN endpoint's Max Packet Size.

In such cases, the device sends a ZLP to indicate end-of-transfer, but libusb does not implicitly consume it because it only reads the number of bytes requested. Therefore, we must explicitly read and discard the ZLP, similar to what we already do for USB writes. example: read 512/512 (data) + read 0/512 (ZLP)

Unconditionally expecting an empty packet was not an issue with USB 2.0, where the typical 512-byte sector size matches the bulk IN endpoint's Max Packet Size. However, with USB SuperSpeed (Max Packet Size = 1024), attempting to read an empty packet after a 512/1024-byte transfer is unnecessary and fails (timeout), resulting in errors during storage device sector size discovery:

waiting for programmer...
qdl: failed to read: Resource temporarily unavailable

Fix and move ZLP handling to bus-specific code (usb.c).

Reported-by: Danilo Leo d.leo@arduino.cc

The assumption that every other Firehose read transfer is null is
incorrect. A Zero-Length Packet (ZLP) is only sent when the received
transfer size is a multiple of the USB IN endpoint's Max Packet Size.
This is a way for the device to indicated end-of-transfer.

Unconditionally expecting an empty packet was not an issue with USB 2.0,
where the typical 512-byte sector size matches the bulk IN endpoint's
Max Packet Size. However, with USB SuperSpeed (Max Packet Size = 1024),
attempting to read an empty packet after a 512/1024-byte transfer is
unnecessary and fails (timeout), resulting in errors during storage
device sector size discovery:
```
waiting for programmer...
qdl: failed to read: Resource temporarily unavailable
```

Fix and move ZLP handling to bus-specific code (usb.c).

Reported-by: Danilo Leo <d.leo@arduino.cc>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
@andersson
Copy link
Collaborator

Patch looks very reasonable.

But how come I have not hit this myself? Have I only been testing the read path in HighSpeed?
How do I reproduce this problem?

@loicpoulain
Copy link
Author

loicpoulain commented Dec 15, 2025

But how come I have not hit this myself? Have I only been testing the read path in HighSpeed? How do I reproduce this problem?

Good question, I believe the answer depends on the specific board, USB cable, and the host’s capabilities. Some boards might not support USB SuperSpeed (SS) lines directly from QDL, as they may require the USB-C controller or muxer to be configured first by the bootloader or the OS. On the other hand, USB High-Speed (HS) lines (DP/DM) are typically routed directly to the SoC/PHY. In our case the issue was with the Arduino UNO-Q, for which the USB-C controller can operate autonomously without additional configuration.

We can check that DUT is detected under SuperSpeed/1Gbps port

 lsusb -t
/:  Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/2p, 10000M
    |__ Port 002: Dev 007, If 0, Class=Vendor Specific Class, Driver=qcserial, 5000M

And that endpoint packet size if actually 1024-byte:

lsusb -v -d 05c6:9008 | grep wMaxPacketSize
        wMaxPacketSize     0x0400  1x 1024 bytes
        wMaxPacketSize     0x0400  1x 1024 bytes

In Superspeed case (max-paket=1024), we can see that the 0-byte URB read following the 512-byte one, is failing:
Screenshot from 2025-12-15 11-47-35

While in the HS case (max-packet=512), the ZLP is indeed received:
Screenshot from 2025-12-15 12-10-18

@andersson andersson merged commit b9ad4ce into linux-msm:master Dec 16, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants