29
29
#include " hal/usb_serial_jtag_ll.h"
30
30
#pragma GCC diagnostic warning "-Wvolatile"
31
31
#include " rom/ets_sys.h"
32
- #include " driver/usb_serial_jtag.h"
33
32
34
33
ESP_EVENT_DEFINE_BASE (ARDUINO_HW_CDC_EVENTS);
35
34
@@ -40,8 +39,11 @@ static intr_handle_t intr_handle = NULL;
40
39
static SemaphoreHandle_t tx_lock = NULL ;
41
40
static volatile bool connected = false ;
42
41
42
+ static volatile unsigned long lastSOF_ms;
43
+ static volatile uint8_t SOF_TIMEOUT;
44
+
43
45
// timeout has no effect when USB CDC is unplugged
44
- static uint32_t requested_tx_timeout_ms = 100 ;
46
+ static uint32_t tx_timeout_ms = 100 ;
45
47
46
48
static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL ;
47
49
@@ -77,7 +79,7 @@ static void hw_cdc_isr_handler(void *arg) {
77
79
78
80
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
79
81
// Interrupt tells us the host picked up the data we sent.
80
- if (!usb_serial_jtag_is_connected ()) {
82
+ if (!HWCDC::isPlugged ()) {
81
83
connected = false ;
82
84
usb_serial_jtag_ll_clr_intsts_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
83
85
// USB is unplugged, nothing to be done here
@@ -132,19 +134,31 @@ static void hw_cdc_isr_handler(void *arg) {
132
134
connected = false ;
133
135
}
134
136
137
+ if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SOF) {
138
+ usb_serial_jtag_ll_clr_intsts_mask (USB_SERIAL_JTAG_INTR_SOF);
139
+ lastSOF_ms = millis ();
140
+ }
141
+
135
142
if (xTaskWoken == pdTRUE) {
136
143
portYIELD_FROM_ISR ();
137
144
}
138
145
}
139
146
147
+ inline bool HWCDC::isPlugged (void ) {
148
+ return (lastSOF_ms + SOF_TIMEOUT) >= millis ();
149
+ }
150
+
140
151
bool HWCDC::isCDC_Connected () {
141
152
static bool running = false ;
142
153
143
154
// USB may be unplugged
144
- if (usb_serial_jtag_is_connected () == false ) {
155
+ if (! isPlugged () ) {
145
156
connected = false ;
146
157
running = false ;
158
+ SOF_TIMEOUT = 5 ; // SOF timeout when unplugged
147
159
return false ;
160
+ } else {
161
+ SOF_TIMEOUT = 50 ; // SOF timeout when plugged
148
162
}
149
163
150
164
if (connected) {
@@ -155,21 +169,72 @@ bool HWCDC::isCDC_Connected() {
155
169
if (running == false && !connected) { // enables it only once!
156
170
usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
157
171
}
172
+
158
173
// this will feed CDC TX FIFO to trigger IN_EMPTY
159
- uint8_t c = ' \0 ' ;
160
- usb_serial_jtag_ll_write_txfifo (&c, sizeof (c));
161
174
usb_serial_jtag_ll_txfifo_flush ();
162
175
running = true ;
163
176
return false ;
164
177
}
165
178
179
+ static void flushTXBuffer (const uint8_t *buffer, size_t size) {
180
+ if (!tx_ring_buf) {
181
+ return ;
182
+ }
183
+ UBaseType_t uxItemsWaiting = 0 ;
184
+ vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
185
+ size_t freeSpace = xRingbufferGetCurFreeSize (tx_ring_buf);
186
+ size_t ringbufferLength = freeSpace + uxItemsWaiting;
187
+
188
+ if (buffer == NULL ) {
189
+ // just flush the whole ring buffer and exit - used by HWCDC::flush()
190
+ size_t queued_size = 0 ;
191
+ uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo (tx_ring_buf, &queued_size, 0 , ringbufferLength);
192
+ if (queued_size && queued_buff != NULL ) {
193
+ vRingbufferReturnItem (tx_ring_buf, (void *)queued_buff);
194
+ }
195
+ return ;
196
+ }
197
+ if (size == 0 ) {
198
+ return ; // nothing to do
199
+ }
200
+ if (freeSpace >= size) {
201
+ // there is enough space, just add the data to the ring buffer
202
+ if (xRingbufferSend (tx_ring_buf, (void *)buffer, size, 0 ) != pdTRUE) {
203
+ return ;
204
+ }
205
+ } else {
206
+ // how many byte should be flushed to make space for the new data
207
+ size_t to_flush = size - freeSpace;
208
+ if (to_flush > ringbufferLength) {
209
+ to_flush = ringbufferLength;
210
+ }
211
+ size_t queued_size = 0 ;
212
+ uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo (tx_ring_buf, &queued_size, 0 , to_flush);
213
+ if (queued_size && queued_buff != NULL ) {
214
+ vRingbufferReturnItem (tx_ring_buf, (void *)queued_buff);
215
+ }
216
+ // now add the new data that fits into the ring buffer
217
+ uint8_t *bptr = (uint8_t *)buffer;
218
+ if (size >= ringbufferLength) {
219
+ size = ringbufferLength;
220
+ bptr = (uint8_t *)buffer + (size - ringbufferLength);
221
+ }
222
+ if (xRingbufferSend (tx_ring_buf, (void *)bptr, size, 0 ) != pdTRUE) {
223
+ return ;
224
+ }
225
+ }
226
+ // flushes CDC FIFO
227
+ usb_serial_jtag_ll_txfifo_flush ();
228
+ }
229
+
166
230
static void ARDUINO_ISR_ATTR cdc0_write_char (char c) {
167
231
if (tx_ring_buf == NULL ) {
168
232
return ;
169
233
}
170
- uint32_t tx_timeout_ms = 0 ;
171
- if (HWCDC::isConnected ()) {
172
- tx_timeout_ms = requested_tx_timeout_ms;
234
+ if (!HWCDC::isConnected ()) {
235
+ // just pop/push RingBuffer and apply FIFO policy
236
+ flushTXBuffer ((const uint8_t *)&c, 1 );
237
+ return ;
173
238
}
174
239
if (xPortInIsrContext ()) {
175
240
xRingbufferSendFromISR (tx_ring_buf, (void *)(&c), 1 , NULL );
@@ -182,6 +247,8 @@ static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
182
247
HWCDC::HWCDC () {
183
248
perimanSetBusDeinit (ESP32_BUS_TYPE_USB_DM, HWCDC::deinit);
184
249
perimanSetBusDeinit (ESP32_BUS_TYPE_USB_DP, HWCDC::deinit);
250
+ lastSOF_ms = 0 ;
251
+ SOF_TIMEOUT = 5 ;
185
252
}
186
253
187
254
HWCDC::~HWCDC () {
@@ -234,9 +301,9 @@ void HWCDC::begin(unsigned long baud) {
234
301
log_e (" HW CDC RX Buffer error" );
235
302
}
236
303
}
237
- // TX Buffer default has 16 bytes if not preset
304
+ // TX Buffer default has 256 bytes if not preset
238
305
if (tx_ring_buf == NULL ) {
239
- if (!setTxBufferSize (16 )) {
306
+ if (!setTxBufferSize (256 )) {
240
307
log_e (" HW CDC TX Buffer error" );
241
308
}
242
309
}
@@ -265,7 +332,9 @@ void HWCDC::begin(unsigned long baud) {
265
332
// Enable USB pad function
266
333
USB_SERIAL_JTAG.conf0 .usb_pad_enable = 1 ;
267
334
usb_serial_jtag_ll_disable_intr_mask (USB_SERIAL_JTAG_LL_INTR_MASK);
268
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
335
+ usb_serial_jtag_ll_ena_intr_mask (
336
+ USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET | USB_SERIAL_JTAG_INTR_SOF
337
+ );
269
338
if (!intr_handle && esp_intr_alloc (ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0 , hw_cdc_isr_handler, NULL , &intr_handle) != ESP_OK) {
270
339
isr_log_e (" HW USB CDC failed to init interrupts" );
271
340
end ();
@@ -300,7 +369,7 @@ void HWCDC::end() {
300
369
}
301
370
302
371
void HWCDC::setTxTimeoutMs (uint32_t timeout) {
303
- requested_tx_timeout_ms = timeout;
372
+ tx_timeout_ms = timeout;
304
373
}
305
374
306
375
/*
@@ -323,13 +392,9 @@ size_t HWCDC::setTxBufferSize(size_t tx_queue_len) {
323
392
}
324
393
325
394
int HWCDC::availableForWrite (void ) {
326
- uint32_t tx_timeout_ms = 0 ;
327
395
if (tx_ring_buf == NULL || tx_lock == NULL ) {
328
396
return 0 ;
329
397
}
330
- if (HWCDC::isCDC_Connected ()) {
331
- tx_timeout_ms = requested_tx_timeout_ms;
332
- }
333
398
if (xSemaphoreTake (tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
334
399
return 0 ;
335
400
}
@@ -338,76 +403,75 @@ int HWCDC::availableForWrite(void) {
338
403
return a;
339
404
}
340
405
341
- static void flushTXBuffer () {
342
- if (!tx_ring_buf) {
343
- return ;
344
- }
345
- UBaseType_t uxItemsWaiting = 0 ;
346
- vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
347
-
348
- size_t queued_size = 0 ;
349
- uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo (tx_ring_buf, &queued_size, 0 , uxItemsWaiting);
350
- if (queued_size && queued_buff != NULL ) {
351
- vRingbufferReturnItem (tx_ring_buf, (void *)queued_buff);
352
- }
353
- // flushes CDC FIFO
354
- usb_serial_jtag_ll_txfifo_flush ();
355
- }
356
-
357
406
size_t HWCDC::write (const uint8_t *buffer, size_t size) {
358
- uint32_t tx_timeout_ms = 0 ;
359
407
if (buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL ) {
360
408
return 0 ;
361
409
}
362
- if (HWCDC::isCDC_Connected ()) {
363
- tx_timeout_ms = requested_tx_timeout_ms;
364
- } else {
365
- connected = false ;
366
- }
367
410
if (xSemaphoreTake (tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
368
411
return 0 ;
369
412
}
370
- size_t space = xRingbufferGetCurFreeSize (tx_ring_buf);
371
- size_t to_send = size, so_far = 0 ;
372
-
373
- if (space > size) {
374
- space = size;
375
- }
376
- // Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR.
377
- if (space > 0 && xRingbufferSend (tx_ring_buf, (void *)(buffer), space, 0 ) != pdTRUE) {
378
- size = 0 ;
413
+ if (!isCDC_Connected ()) {
414
+ // just pop/push RingBuffer and apply FIFO policy
415
+ flushTXBuffer (buffer, size);
379
416
} else {
380
- to_send -= space;
381
- so_far += space;
382
- // Now trigger the ISR to read data from the ring buffer.
383
- usb_serial_jtag_ll_txfifo_flush ();
384
- if (connected) {
385
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
386
- }
417
+ size_t space = xRingbufferGetCurFreeSize (tx_ring_buf);
418
+ size_t to_send = size, so_far = 0 ;
387
419
388
- while (to_send) {
389
- space = xRingbufferGetCurFreeSize (tx_ring_buf);
390
- if (space > to_send) {
391
- space = to_send;
392
- }
393
- // Blocking method, Sending data to ringbuffer, and handle the data in ISR.
394
- if (xRingbufferSend (tx_ring_buf, (void *)(buffer + so_far), space, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE) {
395
- size = so_far;
396
- break ;
397
- }
398
- so_far += space;
420
+ if (space > size) {
421
+ space = size;
422
+ }
423
+ // Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR.
424
+ if (space > 0 && xRingbufferSend (tx_ring_buf, (void *)(buffer), space, 0 ) != pdTRUE) {
425
+ size = 0 ;
426
+ } else {
399
427
to_send -= space;
428
+ so_far += space;
400
429
// Now trigger the ISR to read data from the ring buffer.
401
430
usb_serial_jtag_ll_txfifo_flush ();
402
431
if (connected) {
403
432
usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
404
433
}
434
+ // tracks CDC trasmission progress to avoid hanging if CDC is unplugged while still sending data
435
+ size_t last_toSend = to_send;
436
+ uint32_t tries = tx_timeout_ms; // waits 1ms per sending data attempt, in case CDC is unplugged
437
+ while (connected && to_send) {
438
+ space = xRingbufferGetCurFreeSize (tx_ring_buf);
439
+ if (space > to_send) {
440
+ space = to_send;
441
+ }
442
+ // Blocking method, Sending data to ringbuffer, and handle the data in ISR.
443
+ if (xRingbufferSend (tx_ring_buf, (void *)(buffer + so_far), space, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE) {
444
+ size = so_far;
445
+ log_w (" write failed due to ring buffer full - timeout" );
446
+ break ;
447
+ }
448
+ so_far += space;
449
+ to_send -= space;
450
+ // Now trigger the ISR to read data from the ring buffer.
451
+ usb_serial_jtag_ll_txfifo_flush ();
452
+ if (connected) {
453
+ usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
454
+ }
455
+ if (last_toSend == to_send) {
456
+ // no progress in sending data... USB CDC is probably unplugged
457
+ tries--;
458
+ delay (1 );
459
+ } else {
460
+ last_toSend = to_send;
461
+ tries = tx_timeout_ms; // reset the timeout
462
+ }
463
+ if (tries == 0 ) { // CDC isn't connected anymore...
464
+ size = so_far;
465
+ log_w (" write failed due to waiting USB Host - timeout" );
466
+ connected = false ;
467
+ }
468
+ }
469
+ }
470
+ // CDC was diconnected while sending data ==> flush the TX buffer keeping the last data
471
+ if (to_send && !usb_serial_jtag_ll_txfifo_writable ()) {
472
+ connected = false ;
473
+ flushTXBuffer (buffer + so_far, to_send);
405
474
}
406
- }
407
- // CDC is disconnected ==> flush all data from TX buffer
408
- if (to_send && !usb_serial_jtag_ll_txfifo_writable ()) {
409
- connected = false ;
410
- flushTXBuffer ();
411
475
}
412
476
xSemaphoreGive (tx_lock);
413
477
return size;
@@ -418,39 +482,40 @@ size_t HWCDC::write(uint8_t c) {
418
482
}
419
483
420
484
void HWCDC::flush (void ) {
421
- uint32_t tx_timeout_ms = 0 ;
422
485
if (tx_ring_buf == NULL || tx_lock == NULL ) {
423
486
return ;
424
487
}
425
- if (HWCDC::isCDC_Connected ()) {
426
- tx_timeout_ms = requested_tx_timeout_ms;
427
- } else {
428
- connected = false ;
429
- }
430
488
if (xSemaphoreTake (tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
431
489
return ;
432
490
}
433
- UBaseType_t uxItemsWaiting = 0 ;
434
- vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
435
- if (uxItemsWaiting) {
436
- // Now trigger the ISR to read data from the ring buffer.
437
- usb_serial_jtag_ll_txfifo_flush ();
438
- if (connected) {
439
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
440
- }
441
- }
442
- uint8_t tries = 3 ;
443
- while (tries && uxItemsWaiting) {
444
- delay (5 );
445
- UBaseType_t lastUxItemsWaiting = uxItemsWaiting;
491
+ if (!isCDC_Connected ()) {
492
+ flushTXBuffer (NULL , 0 );
493
+ } else {
494
+ UBaseType_t uxItemsWaiting = 0 ;
446
495
vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
447
- if (lastUxItemsWaiting == uxItemsWaiting) {
448
- tries--;
496
+ if (uxItemsWaiting) {
497
+ // Now trigger the ISR to read data from the ring buffer.
498
+ usb_serial_jtag_ll_txfifo_flush ();
499
+ if (connected) {
500
+ usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
501
+ }
502
+ }
503
+ uint32_t tries = tx_timeout_ms; // waits 1ms per ISR sending data attempt, in case CDC is unplugged
504
+ while (connected && tries && uxItemsWaiting) {
505
+ delay (1 );
506
+ UBaseType_t lastUxItemsWaiting = uxItemsWaiting;
507
+ vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
508
+ if (lastUxItemsWaiting == uxItemsWaiting) {
509
+ tries--;
510
+ }
511
+ if (connected) {
512
+ usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
513
+ }
514
+ }
515
+ if (tries == 0 ) { // CDC isn't connected anymore...
516
+ connected = false ;
517
+ flushTXBuffer (NULL , 0 ); // flushes all TX Buffer
449
518
}
450
- }
451
- if (tries == 0 ) { // CDC isn't connected anymore...
452
- connected = false ;
453
- flushTXBuffer ();
454
519
}
455
520
xSemaphoreGive (tx_lock);
456
521
}
0 commit comments