Skip to content

Commit c2a645d

Browse files
committed
Do not use tx_complete. It is not reliable
1 parent 855d1b1 commit c2a645d

File tree

2 files changed

+82
-90
lines changed

2 files changed

+82
-90
lines changed

cores/esp32/USBCDC.cpp

+76-86
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void tud_cdc_rx_cb(uint8_t itf)
6262

6363
// Invoked when received send break
6464
void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms){
65-
//isr_log_v("itf: %u, duration_ms: %u", itf, duration_ms);
65+
//log_v("itf: %u, duration_ms: %u", itf, duration_ms);
6666
}
6767

6868
// Invoked when space becomes available in TX buffer
@@ -72,56 +72,6 @@ void tud_cdc_tx_complete_cb(uint8_t itf){
7272
}
7373
}
7474

75-
static size_t ring_buf_flush(uint8_t itf, RingbufHandle_t ring_buf){
76-
if(!tud_cdc_n_connected(itf)){
77-
return 0;
78-
}
79-
size_t max_size = tud_cdc_n_write_available(itf);
80-
size_t queued_size = 0;
81-
if(max_size == 0){
82-
if(tud_cdc_n_write_flush(itf) > 0){
83-
// no space but we were able to flush some out
84-
max_size = tud_cdc_n_write_available(itf);
85-
}
86-
if(max_size == 0){
87-
// no space and could not flush
88-
return 0;
89-
}
90-
}
91-
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(ring_buf, &queued_size, 0, max_size);
92-
if (queued_buff == NULL) {
93-
//log_d("nothing to send");
94-
return 0;
95-
}
96-
size_t sent = tud_cdc_n_write(itf, queued_buff, queued_size);
97-
if(sent && sent < max_size){
98-
tud_cdc_n_write_flush(itf);
99-
}
100-
vRingbufferReturnItem(ring_buf, queued_buff);
101-
return sent;
102-
}
103-
104-
static size_t ring_buf_write_nb(uint8_t itf, RingbufHandle_t ring_buf, const uint8_t *buffer, size_t size){
105-
if(buffer == NULL || ring_buf == NULL || size == 0){
106-
return 0;
107-
}
108-
size_t space = xRingbufferGetCurFreeSize(ring_buf);
109-
if(space == 0){
110-
// ring buff is full, maybe we can flush some?
111-
if(ring_buf_flush(itf, ring_buf)){
112-
space = xRingbufferGetCurFreeSize(ring_buf);
113-
}
114-
}
115-
if(size > space){
116-
size = space;
117-
}
118-
if(size && xRingbufferSend(ring_buf, (void*) (buffer), size, 0) != pdTRUE){
119-
return 0;
120-
}
121-
ring_buf_flush(itf, ring_buf);
122-
return size;
123-
}
124-
12575
static void ARDUINO_ISR_ATTR cdc0_write_char(char c){
12676
if(devices[0] != NULL){
12777
devices[0]->write(c);
@@ -132,7 +82,20 @@ static void usb_unplugged_cb(void* arg, esp_event_base_t event_base, int32_t eve
13282
((USBCDC*)arg)->_onUnplugged();
13383
}
13484

135-
USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_ring_buf(NULL) {
85+
USBCDC::USBCDC(uint8_t itfn)
86+
: itf(itfn)
87+
, bit_rate(0)
88+
, stop_bits(0)
89+
, parity(0)
90+
, data_bits(0)
91+
, dtr(false)
92+
, rts(false)
93+
, connected(false)
94+
, reboot_enable(true)
95+
, rx_queue(NULL)
96+
, tx_lock(NULL)
97+
, tx_timeout_ms(250)
98+
{
13699
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
137100
if(itf < MAX_USB_CDC_DEVICES){
138101
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
@@ -165,25 +128,12 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
165128
return rx_queue_len;
166129
}
167130

168-
size_t USBCDC::setTxBufferSize(size_t tx_queue_len){
169-
if(tx_ring_buf){
170-
if(!tx_queue_len){
171-
vRingbufferDelete(tx_ring_buf);
172-
tx_ring_buf = NULL;
173-
}
174-
return 0;
175-
}
176-
tx_ring_buf = xRingbufferCreate(tx_queue_len, RINGBUF_TYPE_BYTEBUF);
177-
if(!tx_ring_buf){
178-
return 0;
179-
}
180-
return tx_queue_len;
181-
}
182-
183131
void USBCDC::begin(unsigned long baud)
184132
{
133+
if(tx_lock == NULL) {
134+
tx_lock = xSemaphoreCreateMutex();
135+
}
185136
setRxBufferSize(256);//default if not preset
186-
setTxBufferSize(256);//default if not preset
187137
devices[itf] = this;
188138
}
189139

@@ -192,7 +142,13 @@ void USBCDC::end()
192142
connected = false;
193143
devices[itf] = NULL;
194144
setRxBufferSize(0);
195-
setTxBufferSize(0);
145+
if(tx_lock != NULL) {
146+
vSemaphoreDelete(tx_lock);
147+
}
148+
}
149+
150+
void USBCDC::setTxTimeoutMs(uint32_t timeout){
151+
tx_timeout_ms = timeout;
196152
}
197153

198154
void USBCDC::_onUnplugged(void){
@@ -301,7 +257,6 @@ void USBCDC::_onRX(){
301257
}
302258

303259
void USBCDC::_onTX(){
304-
ring_buf_flush(itf, tx_ring_buf);
305260
arduino_usb_cdc_event_data_t p = {0};
306261
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_TX_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
307262
}
@@ -360,38 +315,73 @@ size_t USBCDC::read(uint8_t *buffer, size_t size)
360315

361316
void USBCDC::flush(void)
362317
{
363-
if(itf >= MAX_USB_CDC_DEVICES || tx_ring_buf == NULL){
318+
if(itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || !tud_cdc_n_connected(itf)){
364319
return;
365320
}
366-
UBaseType_t uxItemsWaiting = 0;
367-
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
368-
while(uxItemsWaiting){
369-
ring_buf_flush(itf, tx_ring_buf);
370-
delay(5);
371-
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
321+
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
322+
return;
372323
}
324+
tud_cdc_n_write_flush(itf);
325+
xSemaphoreGive(tx_lock);
373326
}
374327

375328
int USBCDC::availableForWrite(void)
376329
{
377-
if(itf >= MAX_USB_CDC_DEVICES || tx_ring_buf == NULL){
330+
if(itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || !tud_cdc_n_connected(itf)){
331+
return 0;
332+
}
333+
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
378334
return 0;
379335
}
380-
return xRingbufferGetCurFreeSize(tx_ring_buf);
336+
size_t a = tud_cdc_n_write_available(itf);
337+
xSemaphoreGive(tx_lock);
338+
return a;
381339
}
382340

383341
size_t USBCDC::write(const uint8_t *buffer, size_t size)
384342
{
385-
if(itf >= MAX_USB_CDC_DEVICES || tx_ring_buf == NULL){
343+
if(itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || buffer == NULL || size == 0 || !tud_cdc_n_connected(itf)){
386344
return 0;
387345
}
388-
size_t sent = ring_buf_write_nb(itf, tx_ring_buf, buffer, size);
389-
if(sent < size){
390-
//log_w("sent %u less bytes", size - sent);
391-
//maybe wait to send the rest?
392-
//xRingbufferSend(tx_ring_buf, (void*) (buffer+sent), size-sent, timeout_ms / portTICK_PERIOD_MS)
346+
if(xPortInIsrContext()){
347+
BaseType_t taskWoken = false;
348+
if(xSemaphoreTakeFromISR(tx_lock, &taskWoken) != pdPASS){
349+
return 0;
350+
}
351+
} else if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
352+
return 0;
353+
}
354+
size_t to_send = size, so_far = 0;
355+
while(to_send){
356+
if(!tud_cdc_n_connected(itf)){
357+
size = so_far;
358+
break;
359+
}
360+
size_t space = tud_cdc_n_write_available(itf);
361+
if(!space){
362+
tud_cdc_n_write_flush(itf);
363+
continue;
364+
}
365+
if(space > to_send){
366+
space = to_send;
367+
}
368+
size_t sent = tud_cdc_n_write(itf, buffer+so_far, space);
369+
if(sent){
370+
so_far += sent;
371+
to_send -= sent;
372+
tud_cdc_n_write_flush(itf);
373+
} else {
374+
size = so_far;
375+
break;
376+
}
393377
}
394-
return sent;
378+
if(xPortInIsrContext()){
379+
BaseType_t taskWoken = false;
380+
xSemaphoreGiveFromISR(tx_lock, &taskWoken);
381+
} else {
382+
xSemaphoreGive(tx_lock);
383+
}
384+
return size;
395385
}
396386

397387
size_t USBCDC::write(uint8_t c)

cores/esp32/USBCDC.h

+6-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
#include <inttypes.h>
2020
#include "esp_event.h"
2121
#include "freertos/FreeRTOS.h"
22-
#include "freertos/ringbuf.h"
22+
#include "freertos/queue.h"
23+
#include "freertos/semphr.h"
2324
#include "Stream.h"
2425

2526
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS);
@@ -60,8 +61,8 @@ class USBCDC: public Stream
6061
void onEvent(esp_event_handler_t callback);
6162
void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
6263

63-
size_t setRxBufferSize(size_t);
64-
size_t setTxBufferSize(size_t);
64+
size_t setRxBufferSize(size_t size);
65+
void setTxTimeoutMs(uint32_t timeout);
6566
void begin(unsigned long baud=0);
6667
void end();
6768

@@ -128,7 +129,8 @@ class USBCDC: public Stream
128129
bool connected;
129130
bool reboot_enable;
130131
xQueueHandle rx_queue;
131-
RingbufHandle_t tx_ring_buf;
132+
xSemaphoreHandle tx_lock;
133+
uint32_t tx_timeout_ms;
132134

133135
};
134136

0 commit comments

Comments
 (0)