Skip to content

ZLP not sent in USB device #15384

Closed
Closed
@daniel-starke

Description

@daniel-starke

Description of defect

Sending out a 64 byte buffer on USB CDC does not show up on the receiving side until more data with a length != 64 bytes is sent.
The USB protocol is a physical 1:1 half-duplex bus protocol with multiple channels (a.k.a. endpoints). That means only one side can send at a time. The device defines a buffer size for each endpoint. Any data larger than this needs to be split into multiple packets. To form a transaction and block the bus until end of the data transmission pakets with the maximum size for the endpoint are sent. The end of the transaction is signalled by sending a packet with less than the maximum size. A zero-length packet (ZLP) can be sent in case no more data is available but the complete data size was a multiple of the maximum endpoint buffer size.
STM32 does not automatically sends out ZLPs at the end of a trancaction as it has no knowledge about it. This needs to be done in the USB device driver. The user can not do so as the driver prevents transmissions of zero length. See line 402

void USBCDC::_send_isr_start()
{
assert_locked();
if (!_tx_in_progress && _tx_size) {
if (USBDevice::write_start(_bulk_in, _tx_buffer, _tx_size)) {
_tx_in_progress = true;
}
}
}

As this appears to be a USB protocol issue instead of a STM32 HAL specific one I would suggest solving it in

void USBDevice::in(usb_ep_t endpoint)
{
assert_locked();
if (!EP_INDEXABLE(endpoint)) {
#if MBED_TRAP_ERRORS_ENABLED
MBED_ERROR(
MBED_MAKE_ERROR(
MBED_MODULE_DRIVER_USB,
MBED_ERROR_CODE_INVALID_INDEX
),
"The endpoint is not indexable."
);
#else
return;
#endif // MBED_TRAP_ERRORS_ENABLED
}

However, I am not aware of how other device platforms handle this. It may be necessary to send a ZLP if the last packet was a multiple of the maximum endpoint buffer size and track its transmission state in

void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
USBPhyHw *priv = ((USBPhyHw *)(hpcd->pData));
uint8_t endpoint = LOG_IN_TO_EP(epnum);
priv->epComplete[EP_TO_IDX(endpoint)] = 1;
if (epnum) {
priv->events->in(endpoint);
} else {
priv->events->ep0_in();
}
}

Target(s) affected by this defect ?

STM32

Toolchain(s) (name and version) displaying this defect ?

PlatformIO latest version

What version of Mbed-os are you using (tag or sha) ?

mbed-os-6.15.0

What version(s) of tools are you using. List all that apply (E.g. mbed-cli)

PlatformIO latest version

How is this defect reproduced ?

Send out 64 bytes after a client connects to a USB CDC port. Read from it on the client side (e.g. by opening the serial device with PuTTY). The 64 bytes of data should show up but don't.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions