-
Notifications
You must be signed in to change notification settings - Fork 25
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.