OpenPrinting / OpenPrinting/ipp-usb
zlp-send quirk should default to true (and perhaps be removed)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 209
- Forks
- 33
- PR merge metrics
- No merged PRs in 30d
Description
The USB ZLP (Zero Length Packet) with Bulk Transfers seems to have been widely misunderstood. Especially because OSes need to offer low-level driver implementation APIs that have the ability to omit sending it. But the ZLP really needs to be generated in various conditions or things will break or have delays in randomly seeming manner.
USB 2.0 specification section 5.8.3 Bulk Transfer Packet Size Constraints says the following:
A bulk transfer is complete when the endpoint does one of the following:
•Has transferred exactly the amount of data expected
•Transfers a packet with a payload size less than wMaxPacketSize or transfers a zero-length packet
When a bulk transfer is complete, the Host Controller retires the current IRP and advances to the next IRP. If a data payload is received that is larger than expected, all pending bulk IRPs for that endpoint will be aborted/retired.
In addition to the above, the host controller offers an IRP level timeout after which the IRP times out. The application may then choose to consider the timeout as a success or an error (e.g. based on the data received).
On Bulk endpoints the transfer typically maps to a read or write operation - also typically on application level a full APDU (e.g. HTTP request) does not need to happen within a single transfer, but it can span multiple transfer. That is, you can write multiple times (do multiple transfers) and the other side then on application level determines based on content when the APDU/HTTP request is complete.
The significant thing is that if the final transfer is a multiple of the USB maximum packet size and the ZLP is not sent, the transfer is not considered complete. While some USB stacks might still handle it. The USB specification is clear that the IRP is not complete, and the application will not see it. And this is a host controller (not OS) related feature. (Think of transfer complete as "flush the buffers from host controller to OS/application".) Without ZLP when needed, the IRP will typically timeout at some point and then it is completely up to the device if it proceeds handling at as valid data, or generating a timeout error. I've seen some USB-IPP devices where this actually depends on what type of request was being made. Other side effects are that print jobs do not print immediately (if any of the IPP requests suffer for this, the timeout needs happen - typically several seconds).
The other problem is that when streaming large amounts of print data, we might end up sending huge transfer sizes. That is we send full USB packets continuously for a long time. The USB specification says that the data payload is larger than expected, all pending bulk IRPs will be aborted/retired. Now some printers handle this fine, but I've seen devices that crash (though this should really be fixed on the device to). USB-IPP does not state maximum transfer size, but the Appendix C there suggests that typically large requests are split to multiple transfers. There is not requirement on boundaries, alignment other matters. When reading IPP-USB the transfers just map to read/write.
I personally think that libusb should have inverted the flag. Most users typically want ZLPs to be automatically generated or things will break in strange ways randomly and depending on device. LIBUSB_TRANSFER_ADD_ZERO_PACKET should really be LIBUSB_TRANSFER_NO_ZERO_PACKET. And have ZLP generation on by default.
But given the following facts on not generating ZLPs:
- is against the "spirit" of USB 5.8.3 and IPP-USB
- will cause random timeouts which different devices handle differently (some work but with additional delay causing perf issues, some error out)
- may cause other compatibility issues during streaming of large print data
I strongly recommend to enable zlp-send by default, or even just removing the quirk.
Thank you for considering.
PS. Perhaps this will also explain more about the received ZLPs. Normally the OS will drain these from received data. But applications can observe them if a they issue bulk read, and the peripheral sends no data (ZLP) as a response. Typically this means the buffers are flushed from the peripheral and no data is pending. But depending on the internal implementation of a peripheral this might be just an implementation quirk. In other cases it can actually mean what it should: there is no more data going to be available. So its always safe to ignore ZLPs, and typically it should be used to indicate that the upper protocol layer should now look into what is happening.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the zlp-send configuration and the USB bulk-transfer handling in the repository, then read how the setting is passed to the underlying transfer layer. Determine whether enabling it by default or removing the quirk preserves existing behavior, and identify the relevant tests or device-level checks before changing the default.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- embedded-iot
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100