Skip to main content
Visitor II
December 24, 2023
Question

USBX CDC ACM data read bug?

  • December 24, 2023
  • 14 replies
  • 6252 views

This behavior seems like a bug.

I’m using USBX CDC ACM on a H723 nucleo board. I’m trying to work with the blocking nature of the ux_device_class_cdc_acm_read. For my application, I would like to get data as soon as up to 256 characters are received but the length will be variable. I’ve tried a few things and found this behavior that seems wrong. My application currently just loops back any strings received.

The endpoint is set to 64 bytes. That was the setting from the example and seems ok. I’ve set the requested_length to 256 and have a buffer allocated to this. This is 4 times the size of the end point.

I’m using Hterm 0.8.9 on Windows 10.
STM32CubeIDE 1.13.2, STM32CubeMX 6.9.2-RC4, STM32Cube MCU Package for H7 1.11.1, X-CUBE-AZRTOS-H7 3.2.0.
ThreadX and USBX is 6.2.1.

When I send a 63 byte string, the read call returns with a length of 63. Data and length are good.

When I send a 65 byte string, the read call returns with a length of 65. Everything is ok.

However, when I send a 64 byte string, the call does not return. I am monitoring the USB traffic with a Beagle 480 USB protocol analyzer. I get an error T which is “Capture for transaction timed out while waiting for additional data.“ If I send an additional single byte, the read call returns with 65 characters. The original 64 and the additional byte.

This certainly seems like a bug as it works at 63 and 65 characters and doesn’t return at 64.

The attached PDF of this has capture information.

    This topic has been closed for replies.

    14 replies

    ST Employee
    December 25, 2023

    Hi @tec683 ,

    Thanks for reaching out ST community.

    could you please confirm that this flags UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP  is defined in ux_user.h in your project ?? 

     

     

    Super User
    December 26, 2023

    The host does not send ZLP.

    @tec683 Have you installed any special driver on Win10 for the USB serial port or Windows automatically recognized the CDC ACM device?

     

    tec683Author
    Visitor II
    December 26, 2023

    I am using the standard Windows serial driver that is installed by default ie no special INF file or anything.

    Yes,  UX_DEVICE_CLASS_CDC_ACM_WRITE_AUTO_ZLP is enabled in the IOC and defined in ux_user.h file. Though reading the comments, this just seems to affect the write behavior and not the read where I'm seeing what seems like anomalous behavior. 

    Super User
    December 28, 2023

    Try another terminal program. Teraterm.

     

    tec683Author
    Visitor II
    December 29, 2023

    I see the same behavior with Teraterm. I would think the Windows CDC ACM driver would be the main cause of behavior which will be the same across different terminal programs.

    Super User
    December 29, 2023

    The Windows driver works for millions of users, every day, with thousands of device models. I cannot figure out why the ZLP is not sent, even with different host applications. Suggest to re-check your endpoint descriptors.

     

     

    tec683Author
    Visitor II
    December 29, 2023

    I didn't say the windows driver was doing anything incorrect.  Just that the terminal program selected was not likely to matter for a low level issue such as I am describing. The terminal programs likely just take the input string and sends it to the COM write function call.  So the behavior is likely the same between programs.

    To your point that the windows driver is likely correct, the problem is more likely in the STM32 firmware. As a practical matter, even if we could somehow prove the Windows USB serial driver was doing something incorrect, the device implementation would have to work around what ever the behavior is because it certainly is not practical to expect all the deployed Windows copies to change anything. 

    I'm new to USB and specifically USBX. In another unanswered post, I could not get the USBX CDC ACM to work on an F7 series processor.  I had success on an H723.  But even on the H723, I found the behavior described here.  I'm hoping that someone much more knowledgeable than me on STM32, USB, and USBX could explain this behavior in more detail.

    Graduate
    November 5, 2024

    Hi, did you get any resolution to this problem?

    I'm seeing the same thing - host PC sends 64 bytes and the USBX stack doesn't respond, whereas 63/65 bytes is fine. From what I see too, the ST/USBX stack is expecting more data or a ZLP but neither the standard Windows 11 serial CDCADM driver, nor Linux, sends one.

    So it would seem to me that this has to be handled in the ST firmware/USBX but isn't. I'm using STM32U585.

    tec683Author
    Visitor II
    November 5, 2024

    I found a messy work around that seems to be working.

    I do a blocking read with the read size set to the end point size of 64 bytes.  This returns if the data sent is less than or equal the end point size.  If less than the end point size, I think the ZLP is working as expected.  If exactly the end point size, I buffer this data and set a 50 mS timer.  If the data transmission is greater than the end point, the subsequent calls return with that data.  If the data returned is less than the end point, that completes the transmission. If another full end point, keep reading.  If the timer times outs, assume the transmission was a multiple of the end point size and consider the transmission complete with the previously buffered data.

    The serial communication in my application is not very high performance.  I'm also sending specific messages so it doesn't have to work for streaming.  I've verified this with terminal programs and my own applications using COM calls.

    A side note, a couple of months ago I noticed there is an USB IO control call that supports setting a timeout on USB CDC reads and writes.  I tried that and while I could do reads with buffers bigger than the end point size, I found it to randomly drop messages.  Since I had the other approach working, I just returned to using it as I didn't have time to dig into this other behavior.

    What I am finding is while the ThreadX implementation is solid as it has been used on a wide variety of platforms and I expect many different projects, the ST contributions to adapt to their processors have not been as reliable or robust.

    Graduate
    November 21, 2024

    Thanks for the reply and sorry for the late response. I'm not so keen on doing the same workaround you have as my requirement is quite high performance. It does seem like a weakness in the ST HAL layer and I guess other USB CDC-ACM stacks build upon the top of that so may well suffer the same issue. Whilst I could work around it in the client (PC) app by perhaps appending a byte if the amount of data sent out ==64, it doesn't seem right.

    I wonder how other MCU platforms (non ST) handle it...

    Would also be helpful if anyone on here from ST can suggest anything?

     

    Super User
    November 21, 2024

    This is exactly the reason why ZLP should be used. If the host requests any number of bytes > endpoint packet size and the device sends multiple of endpoint packet size, the host controller expects more and does not end transaction. This is how USB works. Looks like the USBX driver does not inject ZLPs automatically because the developers thought it is responsibility of user. The driver does not know if, after sending multiple of endpoint packet size, user can send more data or not.

     

     

    Graduate
    November 21, 2024

    Yes, and from the device's point of view this is exactly what happens on tx - the USB device stack is configured to send ZLP when required and the host is happy.

    The problem I'm having is on receive of data from the host (being a Windows or linux PC), which appears not to send a ZLP in this scenario. So the device sits there waiting for more data if the last data packet was exactly 64 bytes. Since the host application is writing to a serial COM/tty port which could be a traditional serial port or CDC-ACM, it doesn't (and shouldn't IMO) know about ZLPs.

     

    tec683Author
    Visitor II
    November 21, 2024

    We certainly can't be the only developers to have encountered this. The Windows driver behavior is what it is and we have to work with it.  Similar situation with Linux.  I'm not in a position to get out my USB bus analyzer to go back and checkout the actual communication. 

    Super User
    November 21, 2024

    We certainly can't be the only developers to have encountered this

    So for the receiving device (STM32) side the found workaround looks good. As soon as the USB controller gets a buffer full (endpoint size) or less - just take that data and handle it. Whether USBX detects a timeout, and how well the  [awesome] ST low level driver interacts with USBX, are details.