hal_usb: Zero-Length Packets for Control IN Transfers
Posted: 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
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