Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes USB CDC setRxBufferSize(), begin(), _onRX() #6413

Merged
merged 11 commits into from Mar 28, 2022

Conversation

SuGlider
Copy link
Collaborator

@SuGlider SuGlider commented Mar 10, 2022

Summary

this PR fixes these issues:
1- USBCDC::setRxBufferSize() doesn't change RX Queue Size
2- USBCDC::begin() always force 256 byte as RX Queue Size
3- USBCDC::_onRX() drops data when RX Queue Size is full
4- USBCDC::_onRX() implies in Arduino Task starvation when receiving more bytes than RX Queue can handle
5- USBCDC::end() resets the board when executed twice in sequence

Impact

This PR allows USBCDC::setRxBufferSize() to be called either, before or after, USBCDC::begin().
It will work in any use case. Data from original RX queue will be copied to the new one, unless it is smaller than the necessary to store previously queued data - in such case an OVERFLOW event will be issued.

It adds a new ARDUINO_USB_CDC_OVERFLOW event that has as parameter the number of bytes lost because of the overflow. Documentation and examples have been updated to reflect this change.

Related links

Fixes #6221
Fixes #5727

@SuGlider SuGlider added Type: Bug 🐛 All bugs Chip: ESP32-S2 Issue is related to support of ESP32-S2 Chip Chip: ESP32-S3 Issue is related to support of ESP32-S3 Chip labels Mar 10, 2022
@SuGlider SuGlider added this to the 2.0.3 milestone Mar 10, 2022
@SuGlider SuGlider self-assigned this Mar 10, 2022
@@ -133,7 +136,8 @@ void USBCDC::begin(unsigned long baud)
if(tx_lock == NULL) {
tx_lock = xSemaphoreCreateMutex();
}
setRxBufferSize(256);//default if not preset
// if rx_queue was set before begin(), keep it
if (!rx_queue) setRxBufferSize(256); //default if not preset
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point is this function to only be called before begin and never after. If you really want to make it changeable, you need to make sure that the old queue is either empty or you copy the data from it to the new one before deletion.

Copy link
Collaborator Author

@SuGlider SuGlider Mar 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I agree that in this case it shall copy data from the old queue to the new one. I'll implement it this way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

for(uint32_t i=0; i<count; i++){
if(rx_queue == NULL || !xQueueSend(rx_queue, buf+i, 0)){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just changing the timeout here from 0 to something else would "fix" this issue. No need for the other changes (and no need for set delay). Can't stop stressing enough that you should use Arduino API when possible, not vTaskDelay

Copy link
Collaborator Author

@SuGlider SuGlider Mar 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, just doing it doesn't fix it because, data may be partially copied from TUD CDC and it will return when the Arduino CDC queue is full, not sending Arduino USB CDC event to the application and dropping data.
It's actually a problem of Race Condition between USB TUD Driver Task (usb_device_task) and the Arduino main Task / user Tasks.

Copy link
Collaborator Author

@SuGlider SuGlider Mar 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding using Arduino delay() or FreeRTOS vTaskDelay(), I think that in this case, it is not about delaying the execution, but actually RTOS Scheduler giving the control to a lower priority Task (Arduino Main Task) in order to allow the consumer Task to do its job.

In the case of this issue, I think it is a lot clearer to use vTaskDelay() instead.
The real issue here is about a sort of Race Condition to the queues and Arduino Task Starvation when a lot of data comes from USB CDC.

For example, executing taskYIELD() doesn't solve the issue because of the very high priority of usb_device_task.
It must pause for a number of Ticks and allow Arduino Task to take place - considering it may consume USB CDC queue, otherwise it will drop data when queue is full. Not sure if it should also flush data in order to keep receiving data and end up with the latest send data in its queue.

Please let me know your thoughts on it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we talking about the same things? Doesn't the code below do exactly what we need?

void USBCDC::_onRX(){
    uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE+1];
    uint32_t count = tud_cdc_n_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE);
    for(uint32_t i=0; i<count; i++){
        if(rx_queue == NULL || !xQueueSend(rx_queue, buf+i, 10)){
            count = i;
            break;
        }
    }
    if (count) {
        arduino_usb_cdc_event_data_t p;
        p.rx.len = count;
        arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_RX_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re delay(): This is Arduino code. We use our APIs when we have them. It calls vTaskDelay in the background also. Calls, implementation, anything can change, but delay() will always delay. Please use Arduino APIs when such are available.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth adding a new event to tell how many bytes were missed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should at least log_e here that something went wrong and bytes were dropped. It could help us in the future as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have created an event ARDUINO_USB_CDC_OVERFLOW that will tell in rx_overflow.dropped_bytes the number of bytes that were lost. Should I also add a log_e or with the event would be enough?
I also added that event to the Library examples.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log_e is always good for us. Will print even if the user is not listening to events

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All Done - including copying queue data in setRxBufferSize()

@SuGlider
Copy link
Collaborator Author

SuGlider commented Mar 17, 2022

@me-no-dev - PR is ready for a final review.

@@ -33,6 +33,7 @@ typedef enum {
ARDUINO_USB_CDC_LINE_CODING_EVENT,
ARDUINO_USB_CDC_RX_EVENT,
ARDUINO_USB_CDC_TX_EVENT,
ARDUINO_USB_CDC_OVERFLOW,
Copy link
Member

@me-no-dev me-no-dev Mar 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's name this ARDUINO_USB_CDC_RX_OVERFLOW_EVENT for clarity (and the option to add TX in the future)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@SuGlider
Copy link
Collaborator Author

@me-no-dev - this PR is ready, just waiting for your final review.

@me-no-dev me-no-dev merged commit 6014ff4 into espressif:master Mar 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Chip: ESP32-S2 Issue is related to support of ESP32-S2 Chip Chip: ESP32-S3 Issue is related to support of ESP32-S3 Chip Type: Bug 🐛 All bugs
Projects
4 participants