firehose/usb: Explicitly handle ZLP on USB read transfers

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>
This commit is contained in:
Loic Poulain
2025-12-01 12:06:47 +01:00
committed by Bjorn Andersson
parent 516a75ff89
commit b9ad4ceaf8
2 changed files with 10 additions and 14 deletions

View File

@@ -642,7 +642,6 @@ static int firehose_issue_read(struct qdl_device *qdl, struct read_op *read_op,
size_t left;
int ret;
int n;
bool expect_empty;
buf = malloc(qdl->max_payload_size);
if (!buf)
@@ -681,8 +680,7 @@ static int firehose_issue_read(struct qdl_device *qdl, struct read_op *read_op,
t0 = time(NULL);
left = read_op->num_sectors;
expect_empty = false;
while (left > 0 || expect_empty) {
while (left > 0) {
chunk_size = MIN(qdl->max_payload_size / sector_size, left);
n = qdl_read(qdl, buf, chunk_size * sector_size, 30000);
@@ -690,13 +688,7 @@ static int firehose_issue_read(struct qdl_device *qdl, struct read_op *read_op,
err(1, "failed to read");
}
if (n == 0 && expect_empty) {
// Every second transfer is empty during this stage.
expect_empty = false;
continue;
} else if (expect_empty) {
err(1, "expected empty transfer but received non-empty transfer during read");
} else if ((size_t)n != chunk_size * sector_size) {
if ((size_t)n != chunk_size * sector_size) {
err(1, "failed to read full sector");
}
@@ -715,10 +707,6 @@ static int firehose_issue_read(struct qdl_device *qdl, struct read_op *read_op,
}
left -= chunk_size;
#ifndef _WIN32
// on mac/linux, every other response is empty
expect_empty = true;
#endif
if (!quiet)
ux_progress("%s", read_op->num_sectors - left, read_op->num_sectors, read_op->filename);

8
usb.c
View File

@@ -244,6 +244,14 @@ static int usb_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int
if (ret == LIBUSB_ERROR_TIMEOUT && actual == 0)
return -ETIMEDOUT;
/* If what we read equals the endpoint's Max Packet Size, consume the ZLP explicitly */
if ((len == actual) && !(actual % qdl_usb->in_maxpktsize)) {
ret = libusb_bulk_transfer(qdl_usb->usb_handle, qdl_usb->in_ep,
NULL, 0, NULL, timeout);
if (ret)
warnx("Unable to read ZLP: %s", libusb_strerror(ret));
}
return actual;
}