hal_usb: Zero-Length Packets for Control IN Transfers

Report here problems in any of ChibiOS components. This forum is NOT for support.
WowSuchName
Posts: 7
Joined: Mon Sep 07, 2020 2:00 pm
Has thanked: 2 times
Been thanked: 2 times

hal_usb: Zero-Length Packets for Control IN Transfers

Postby WowSuchName » Thu May 18, 2023 12:14 pm

Hi everybody,

I stumbled across another piece of the USB driver: for implementing a DFU function (and potentially any other control IN transfer), the device needs to send all data except for the last bytes as packets of the requested size. The host detects the end of the data stream when a short packet is sent. However, if the data is a multiple of the requested packet size, the device should send a zero-length packet (ZLP) to signal the end of transmission.

Please correct me if I'm mistaking, but as far as I can tell, the current USB driver does not support this operation. As far as I can tell, the control flow is as follows:

- LLD handles SETUP token by calling _usb_ep0setup
- USB driver calls user handler (which is present) which in turn configures USBDx.ep0{n,next,cb} using usbSetupTransfer(). At some point, we might need to send a ZLP, so let's say it calls usbSetupTransfer(&USBDx, NULL, 0u, NULL);
- Control flow reaches hal_usb:847. The branch is not taken, as USBDx.ep0n == 0. The driver starts the receive phase (else branch) and the ZLP is never sent.

Again, I might be overlooking the obvious solution, but to me, the driver lacks the ability to explicitly request sending a ZLP from the user request handler. To test this, I changed the driver such that USBDx.ep0n==0 && USBDx.ep0next != NULL signifies this request. However, implementing this would break the API. Here is another variant which introduces a Boolean new field ep0zlp: Link to github.

I tested this on an STM32L1 with custom board for both master and ver21.11.3 and it worked fine using the standard dfu-util on a Linux host. I assume that this will be the same for older versions as well.

Here are the remaining informations:

- ChibiOS version
master / ver21.11.3 (both referring to the github mirror)

- Compiler
gcc version 13.1.0 (Arch Repository)
newlib version 4.3.0.20230120-1 (Arch Repository)

- Platform and board
STM32L151CBT6A on a custom board

- Nature of the problem / Failure Mode
No possibility to send ZLP during USB Control IN transfers.


Cheers,
Tim

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: hal_usb: Zero-Length Packets for Control IN Transfers

Postby Giovanni » Thu May 18, 2023 1:57 pm

Hi,

Haven't touched USB for a while, if I remember well USBs peripherals should do ZLP insertion automatically at the end of the transfer.

Giovanni

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: hal_usb: Zero-Length Packets for Control IN Transfers

Postby Giovanni » Sun May 28, 2023 9:24 am

News? If not I will close it here.

Giovanni

WowSuchName
Posts: 7
Joined: Mon Sep 07, 2020 2:00 pm
Has thanked: 2 times
Been thanked: 2 times

Re: hal_usb: Zero-Length Packets for Control IN Transfers

Postby WowSuchName » Sun May 28, 2023 10:01 am

Hi Giovanni,

sorry, I didn't find the time lately.

The peripherals (at least STM32L1) handle ZLPs just like any other packet: Setting the packet size to zero and marking the endpoint as valid will yield a ZLP at least in the bulk and control cases. This, however, has to be done by the driver. The latter does handle this indeed, but only for packets with non-zero length and zero remainder (see the sequence I sketched out above).

What I'm missing is the possibility to send a ZLP without any data beforehand. To my understanding, this is crucial for implementing the DFU specs correctly. For these, the host repeatedly issues control IN transfers* as long as it does not receive a short or zero-length packet. If the available data is a multiple of the transfer size, the function will have to send a ZLP at one point. The alternative I see would be so enqueue the whole data block and let the current driver implementation handle the rest, but that would only work, if the host always sent the correct control IN transfer. Doesn't sound right to me.


Best regards
Tim

* The specification does not mention if the host should query the function's state between the actual data transfers, which would solve the issue otherwise. However, dfu-util does not do this and waits for the short / ZL packet instead.


Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 12 guests