6
votes

I'm writing firmware for a USB 2.0 full speed device that communicates with a WinUSB host, with one Bulk Pipe in each direction. When should the device send a zero-length packet (ZLP) to terminate an IN transfer, and how does it know that it should?

Section 5.8.3 of the USB 2.0 spec says:

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 [ZLP]

I interpret this to mean that a ZLP should be sent when the transfer size is an integer multiple of the max packet size, and the "expected" size of the transfer is greater than the actual size (i.e. what is available to be sent). But how does the recipient know what's expected?

For instance, I'm using the WinUSBNet wrapper in C#. When I read from the pipe like this

int bytesRead;
buffer = new byte[128];
try
{
   bytesRead = m_PipeIN.Read(buffer);
   buffer = buffer.Take(bytesRead).ToArray();
}

the library calls WinUsb_ReadPipe() like this:

WinUsb_ReadPipe(InterfaceHandle(ifaceIndex),
   pipeID,
   pBuffer + offset,
   (uint)bytesToRead,
   out bytesRead,
   IntPtr.Zero);

Suppose the device has exactly 128 bytes to send, and max packet size is 64 bytes. How does the device determine what the host is "expecting", thus whether it should send a ZLP to terminate the transfer?

(Similar to this question, but that one is about control pipes. I'm asking about bulk pipes.)

2

2 Answers

8
votes

Explanation of the spec:

Case 1

Has transferred exactly the amount of data expected

This means that if the host is expecting X amount of bytes, and you send exactly X amount of bytes, the transfer stops right there. MPS and ZLP don't play into it.


Case 2

Transfers a packet with a payload size less than wMaxPacketSize or transfers a zero-length packet [ZLP]

This means that if the host is expecting X bytes but you want to send only Y bytes, where Y < X, the transfer is complete as soon as you do a "short" packet, a packet less the MPS. If Y bytes is a multiple of MPS, then you would have to do a ZLP.

Example 1 (no ZLP)

MPS = 512, the host expects 8192 bytes.

You want to send only 1500 bytes. The payload would go over in 3 packets like this:

Packet 0: [512 bytes]  MPS
Packet 1: [512 bytes]  MPS
Packet 2: [476 bytes]  short packet

When the host gets the short packet, it knows the transfer is complete, and won't continue asking for more packets for the transfer.

Example 2 (with ZLP)

MPS = 512, the host expects 8192 bytes.

You want to send only 2048 bytes. The payload would go over in 4 packets like this:

Packet 0: [512 bytes]  MPS
Packet 1: [512 bytes]  MPS
Packet 2: [512 bytes]  MPS
Packet 3: [512 bytes]  MPS

At this point, the host has received 4 MPS-sized packets so it doesn't know the transfer is complete. So it will continue to request packets from the device.

Packet 4: [0 bytes]  short packet (ZLP)

When the host gets the short packet, it knows the transfer is complete, and won't continue asking for more packets for the transfer.


Determining Transfer Size

You may be wondering how to determine the "expected" amount of bytes since BULK transfers do not have a length like CTRL transfers do. This is determined entirely by the higher-level protocol that specifies how to do transfers on the BULK pipes. The host and device both follow this protocol and thus they are in sync about how much data to transfer at any given time.

This protocol is typically specified by a class specification, like the mass-storage class protocol, or it could be some very simple protocol of your own design.

0
votes

Transfers a packet with a payload size less than wMaxPacketSize or transfers a zero-length packet [ZLP]

a ZLP has to be send when the length of payload data is exactly an integer multiple of wMaxPacketSize

The USB spec defines that if the last packet of a bulk transfer has the exact size of the endpoint max packet size, the whole transfer must be terminated by a zero length urb.

If apps don't sent this in such a situation libusb times out and the initial urb is never sent resulting in a broken application.

All Kernel drivers use the URB_ZERO_PACKET to comply to the spec correctly.

source: http://libusb.org/ticket/6

in case that data length is exactly an integer multiple of wMaxPacketSize the first ending condition packetSize < wMaxPacketSize does not apply because in this case packetSize = wMaxPacketSize.

so to indicate the information that the last packet has been send you need a ZLP, else the other side would expect more data

there are several other situations when ZLPs are sent see i.e. USB in a nutshell website