USB CDC ACM: RX data gets overwritten in app_usbd_cdc_acm_read_any() at end of data transmission

To handle RX of USB CDC ACM we are polling data using:

ret = app_usbd_cdc_acm_read_any(&m_app_cdc_acm, m_rx_buffer, MIN(BUFFER_SIZE,size));

On

case NRF_SUCCESS:

rx_size = app_usbd_cdc_acm_rx_size(&m_app_cdc_acm);
copy_rx_data(buffer, n, m_rx_buffer);

We assume data has been transferred to our use buffer `m_rx_buffer` and get the data directly.

On

case NRF_ERROR_IO_PENDING:

usb_txrx_state = USB_TXRX_STATE_RX;

We assume data has been NOT been transferred and assume to we get the data in the event handler instead.

void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
app_usbd_cdc_acm_user_event_t event)
{
app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
last_usb_cdc_event = event;
ret_code_t ret;
switch (event)
{
...
case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:

rx_size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
copy_rx_data(buffer, n, m_rx_buffer);

This works well until the end of the data (583 bytes in total) is transferred.

On the NEXT TO LAST call to app_usbd_cdc_acm_read_any() we get NRF_SUCCESS and get correct data.

On the last call to app_usbd_cdc_acm_read_any() we get NRF_ERROR_IO_PENDING, but we see that correct data anyway has still been transferred to m_rx_buffer.

But because we assume there are no data directly available (it should be fetched in event handler) we make an intermediate call to:

while (app_usbd_event_queue_process()) {
}

To finalize the transfer event handler.

Stepping through app_usbd_event_queue_process() we see the following sequence of calls ...

#if (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)
if (p_event_item->sof_cnt > 0)
{
if (p_event_item->start_frame > USBD_FRAMECNTR_FRAMECNTR_Msk)
{
p_event_item->start_frame = 0;
}
sof_event.drv_evt.data.sof.framecnt = (p_event_item->start_frame)++;
--(p_event_item->sof_cnt);
app_usbd_event_execute(&sof_event);
return true;
}
#endif // (APP_USBD_CONFIG_SOF_HANDLING_MODE == APP_USBD_SOF_HANDLING_COMPRESS_QUEUE)

==> app_usbd_event_execute(&(p_event_item->evt));

...

/**
* @brief @ref app_usbd_class_methods_t::event_handler
*/
static ret_code_t cdc_acm_event_handler(app_usbd_class_inst_t const * p_inst,
app_usbd_complex_evt_t const * p_event)
{
ASSERT(p_inst != NULL);
ASSERT(p_event != NULL);

ret_code_t ret = NRF_SUCCESS;
switch (p_event->app_evt.type)
{
...
case APP_USBD_EVT_DRV_EPTRANSFER:
==> ret = cdc_acm_endpoint_ev(p_inst, p_event);
break;

...

static ret_code_t cdc_acm_endpoint_ev(app_usbd_class_inst_t const * p_inst,
app_usbd_complex_evt_t const * p_event)
{

==> ret = cdc_acm_rx_block_finished(p_inst);

...

static ret_code_t cdc_acm_rx_block_finished(app_usbd_class_inst_t const * p_inst)
{

...

==> memcpy(p_cdc_acm_ctx->rx_transfer[0].p_buf,
p_cdc_acm_ctx->internal_rx_buf,
bytes_to_cpy);

The final memcpy finally COPIES OLD DATA from p_cdc_acm_ctx into our user buffer (which contained good data).

So there seems to be some internal USB stack state machine problem here ...

To summarise:

1. Correct data is transferred from app_usbd_cdc_acm_read_any() even if we got NRF_ERROR_IO_PENDING.
2. Consective call app_usbd_event_queue_process() copies old data (i.e. overwrites good data) to user buffer. This should not be possible if USB state contained in p_cdc_acm_ctx is valid.
3. We dont get APP_USBD_CDC_ACM_USER_EVT_RX_DONE event as expected until user buffer is overwritten by app_usbd_event_queue_process()


We are using SDK 17.0.2.

We have enabled APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED, but it does not give any good indication.

One addional note: This is size dependent as well. So if last call returns data pending (which seems to be size dependent) internal buffer seems to be corrupted.

615 OK
593 - 599 OK
581-592 NOK


  • Not recently, but I have worked with the Nordic USB CDC sample on my Windows machine before. The driver should be automatically installed after a few seconds. Could it be that your Windows machine's driver installation is limited due to enterprise firewall or Windows policy?

    I haven't been successful yet. I will try WSL next, not having a Linux machine to try,

    Best regards,

    Hieu

  • Hi Peter,

    I would like to give you an update.

    I tried Windows again with your suggestion here:

    PeterLjung said:
    I think we had to introduce a delay specifically for Windows compared with Linux in another project.
    So you could try to introduce a delay between usb_config() and usb_write() and/or include a set of process_usb_events() between operations.

    But the result has remained the same.

    I also tried to create another setup with WSL. However, I have been having issue passing the USB port to WSL. It seems there are some firewall issues.

    I will try to pull in some other help, preferably someone who is working with Linux.

    Best regards,

    Hieu

  • I tried Nordic USB CDC ACM sample on another Windows machine and there it is detected fine.
    But on the computer that I have access to it doesn't for some reason. The device is not detected at all. 

    1. I looked at BIOS settings to see if devices was disabled
    2. I tested another device (USB stick) on the same port to see if that worked. And yes.
    3. I tried to install nRF utils tools to see if there still was some driver issue.
    4. I tried to power the board through debug USB port to see if power was the issue.

    But it works fine on 3 other machines with different OSes (macOS, OpenBSD and Linux).
    Windows is just too strange. If you could try on Linux it would be great. 

    Best regards / Peter

  • There is one person in my team with a Linux machine, but they are away until next Monday. I will try to get their help then. In the meantime, I will try with a virtual machine.

    Best regards,

    Hieu

  • I did a test on another Windows machine and there USB is detected fine. The one I am using does not work. But I also noticed that serial ports COM ports are rather written as a number rather than a device name as in Linux. 
    Also I cannot detect if device is connected on Windows which was discussed above. So the RW protocol does not work on Windows.
    I made a quick rewrite that possibly could work on Windows. Device is not detected and script is will not be waiting for a command from the nRF device. I will not be able to test this until later this week (I dont have access to this machine) so it may very well have issues.
    usb-test-packed-3-windows.tgz

    So it is certainly much easier to try on regular Linux machine.

Related