1
- // Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
1
+ // Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
2
2
//
3
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
4
// you may not use this file except in compliance with the License.
28
28
#include " hal/usb_serial_jtag_ll.h"
29
29
#pragma GCC diagnostic warning "-Wvolatile"
30
30
#include " rom/ets_sys.h"
31
+ #include " driver/usb_serial_jtag.h"
31
32
32
33
ESP_EVENT_DEFINE_BASE (ARDUINO_HW_CDC_EVENTS);
33
34
34
35
static RingbufHandle_t tx_ring_buf = NULL ;
35
36
static QueueHandle_t rx_queue = NULL ;
36
37
static uint8_t rx_data_buf[64 ] = {0 };
37
38
static intr_handle_t intr_handle = NULL ;
38
- static volatile bool initial_empty = false ;
39
39
static SemaphoreHandle_t tx_lock = NULL ;
40
+ static volatile bool isConnected = false ;
40
41
41
- // workaround for when USB CDC is not connected
42
- static uint32_t tx_timeout_ms = 0 ;
43
- static bool tx_timeout_change_request = false ;
42
+ // timeout has no effect when USB CDC is unplugged
43
+ static uint32_t requested_tx_timeout_ms = 100 ;
44
44
45
45
static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL ;
46
46
@@ -78,21 +78,17 @@ static void hw_cdc_isr_handler(void *arg) {
78
78
79
79
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
80
80
// Interrupt tells us the host picked up the data we sent.
81
+ if (!usb_serial_jtag_is_connected ()) {
82
+ isConnected = false ;
83
+ usb_serial_jtag_ll_clr_intsts_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
84
+ // USB is unplugged, nothing to be done here
85
+ return ;
86
+ } else {
87
+ isConnected = true ;
88
+ }
81
89
if (usb_serial_jtag_ll_txfifo_writable () == 1 ) {
82
90
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
83
91
usb_serial_jtag_ll_disable_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
84
- if (!initial_empty){
85
- initial_empty = true ;
86
- // First time USB is plugged and the application has not explicitly set TX Timeout, set it to default 100ms.
87
- // Otherwise, USB is still unplugged and the timeout will be kept as Zero in order to avoid any delay in the
88
- // application whenever it uses write() and the TX Queue gets full.
89
- if (!tx_timeout_change_request) {
90
- tx_timeout_ms = 100 ;
91
- }
92
- // send event?
93
- // ets_printf("CONNECTED\n");
94
- arduino_hw_cdc_event_post (ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_CONNECTED_EVENT, &event, sizeof (arduino_hw_cdc_event_data_t ), &xTaskWoken);
95
- }
96
92
size_t queued_size;
97
93
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR (tx_ring_buf, &queued_size, 64 );
98
94
// If the hardware fifo is avaliable, write in it. Otherwise, do nothing.
@@ -102,7 +98,7 @@ static void hw_cdc_isr_handler(void *arg) {
102
98
usb_serial_jtag_ll_write_txfifo (queued_buff, queued_size);
103
99
usb_serial_jtag_ll_txfifo_flush ();
104
100
vRingbufferReturnItemFromISR (tx_ring_buf, queued_buff, &xTaskWoken);
105
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
101
+ if (isConnected) usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
106
102
// send event?
107
103
// ets_printf("TX:%u\n", queued_size);
108
104
event.tx .len = queued_size;
@@ -124,18 +120,15 @@ static void hw_cdc_isr_handler(void *arg) {
124
120
break ;
125
121
}
126
122
}
127
- // send event?
128
- // ets_printf("RX:%u/%u\n", i, rx_fifo_len);
129
123
event.rx .len = i;
130
124
arduino_hw_cdc_event_post (ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof (arduino_hw_cdc_event_data_t ), &xTaskWoken);
125
+ isConnected = true ;
131
126
}
132
127
133
128
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
134
129
usb_serial_jtag_ll_clr_intsts_mask (USB_SERIAL_JTAG_INTR_BUS_RESET);
135
- initial_empty = false ;
136
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
137
- // ets_printf("BUS_RESET\n");
138
130
arduino_hw_cdc_event_post (ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof (arduino_hw_cdc_event_data_t ), &xTaskWoken);
131
+ isConnected = false ;
139
132
}
140
133
141
134
if (xTaskWoken == pdTRUE) {
@@ -144,12 +137,16 @@ static void hw_cdc_isr_handler(void *arg) {
144
137
}
145
138
146
139
static void ARDUINO_ISR_ATTR cdc0_write_char (char c) {
140
+ uint32_t tx_timeout_ms = 0 ;
141
+ if (usb_serial_jtag_is_connected ()) {
142
+ tx_timeout_ms = requested_tx_timeout_ms;
143
+ }
147
144
if (xPortInIsrContext ()){
148
145
xRingbufferSendFromISR (tx_ring_buf, (void *) (&c), 1 , NULL );
149
146
} else {
150
147
xRingbufferSend (tx_ring_buf, (void *) (&c), 1 , tx_timeout_ms / portTICK_PERIOD_MS);
151
148
}
152
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY );
149
+ usb_serial_jtag_ll_txfifo_flush ( );
153
150
}
154
151
155
152
HWCDC::HWCDC () {
@@ -160,9 +157,33 @@ HWCDC::~HWCDC(){
160
157
end ();
161
158
}
162
159
160
+
161
+ // It should return <true> just when USB is plugged and CDC is connected.
163
162
HWCDC::operator bool () const
164
163
{
165
- return initial_empty;
164
+ static bool running = false ;
165
+
166
+ // USB may be unplugged
167
+ if (usb_serial_jtag_is_connected () == false ) {
168
+ isConnected = false ;
169
+ running = false ;
170
+ return false ;
171
+ }
172
+
173
+ if (isConnected) {
174
+ running = false ;
175
+ return true ;
176
+ }
177
+
178
+ if (running == false && !isConnected) { // enables it only once!
179
+ usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
180
+ }
181
+ // this will feed CDC TX FIFO to trigger IN_EMPTY
182
+ uint8_t c = ' \0 ' ;
183
+ usb_serial_jtag_ll_write_txfifo (&c, sizeof (c));
184
+ usb_serial_jtag_ll_txfifo_flush ();
185
+ running = true ;
186
+ return false ;
166
187
}
167
188
168
189
void HWCDC::onEvent (esp_event_handler_t callback){
@@ -206,9 +227,9 @@ void HWCDC::begin(unsigned long baud)
206
227
log_e (" HW CDC RX Buffer error" );
207
228
}
208
229
}
209
- // TX Buffer default has 256 bytes if not preset
230
+ // TX Buffer default has 16 bytes if not preset
210
231
if (tx_ring_buf == NULL ) {
211
- if (!setTxBufferSize (256 )) {
232
+ if (!setTxBufferSize (16 )) {
212
233
log_e (" HW CDC TX Buffer error" );
213
234
}
214
235
}
@@ -227,8 +248,6 @@ void HWCDC::begin(unsigned long baud)
227
248
} else {
228
249
log_e (" Serial JTAG Pins can't be set into Peripheral Manager." );
229
250
}
230
-
231
- usb_serial_jtag_ll_txfifo_flush ();
232
251
}
233
252
234
253
void HWCDC::end ()
@@ -248,13 +267,11 @@ void HWCDC::end()
248
267
arduino_hw_cdc_event_loop_handle = NULL ;
249
268
}
250
269
HWCDC::deinit (this );
270
+ isConnected = false ;
251
271
}
252
272
253
273
void HWCDC::setTxTimeoutMs (uint32_t timeout){
254
- tx_timeout_ms = timeout;
255
- // it registers that the user has explicitly requested to use a value as TX timeout
256
- // used for the workaround with unplugged USB and TX Queue Full that causes a delay on every write()
257
- tx_timeout_change_request = true ;
274
+ requested_tx_timeout_ms = timeout;
258
275
}
259
276
260
277
/*
@@ -278,9 +295,13 @@ size_t HWCDC::setTxBufferSize(size_t tx_queue_len){
278
295
279
296
int HWCDC::availableForWrite (void )
280
297
{
298
+ uint32_t tx_timeout_ms = 0 ;
281
299
if (tx_ring_buf == NULL || tx_lock == NULL ){
282
300
return 0 ;
283
301
}
302
+ if (usb_serial_jtag_is_connected ()) {
303
+ tx_timeout_ms = requested_tx_timeout_ms;
304
+ }
284
305
if (xSemaphoreTake (tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
285
306
return 0 ;
286
307
}
@@ -289,11 +310,32 @@ int HWCDC::availableForWrite(void)
289
310
return a;
290
311
}
291
312
313
+ static void flushTXBuffer ()
314
+ {
315
+ if (!tx_ring_buf) return ;
316
+ UBaseType_t uxItemsWaiting = 0 ;
317
+ vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
318
+
319
+ size_t queued_size = 0 ;
320
+ uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo (tx_ring_buf, &queued_size, 0 , uxItemsWaiting);
321
+ if (queued_size && queued_buff != NULL ) {
322
+ vRingbufferReturnItem (tx_ring_buf, (void *)queued_buff);
323
+ }
324
+ // flushes CDC FIFO
325
+ usb_serial_jtag_ll_txfifo_flush ();
326
+ }
327
+
292
328
size_t HWCDC::write (const uint8_t *buffer, size_t size)
293
329
{
330
+ uint32_t tx_timeout_ms = 0 ;
294
331
if (buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL ){
295
332
return 0 ;
296
333
}
334
+ if (usb_serial_jtag_is_connected ()) {
335
+ tx_timeout_ms = requested_tx_timeout_ms;
336
+ } else {
337
+ isConnected = false ;
338
+ }
297
339
if (xSemaphoreTake (tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
298
340
return 0 ;
299
341
}
@@ -311,8 +353,9 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
311
353
to_send -= space;
312
354
so_far += space;
313
355
// Now trigger the ISR to read data from the ring buffer.
314
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
315
-
356
+ usb_serial_jtag_ll_txfifo_flush ();
357
+ if (isConnected) usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
358
+
316
359
while (to_send){
317
360
if (max_size > to_send){
318
361
max_size = to_send;
@@ -325,9 +368,15 @@ size_t HWCDC::write(const uint8_t *buffer, size_t size)
325
368
so_far += max_size;
326
369
to_send -= max_size;
327
370
// Now trigger the ISR to read data from the ring buffer.
328
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
371
+ usb_serial_jtag_ll_txfifo_flush ();
372
+ if (isConnected) usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
329
373
}
330
374
}
375
+ // CDC is diconnected ==> flush all data from TX buffer
376
+ if (to_send && !usb_serial_jtag_ll_txfifo_writable ()) {
377
+ isConnected = false ;
378
+ flushTXBuffer ();
379
+ }
331
380
xSemaphoreGive (tx_lock);
332
381
return size;
333
382
}
@@ -339,21 +388,35 @@ size_t HWCDC::write(uint8_t c)
339
388
340
389
void HWCDC::flush (void )
341
390
{
391
+ uint32_t tx_timeout_ms = 0 ;
342
392
if (tx_ring_buf == NULL || tx_lock == NULL ){
343
393
return ;
344
394
}
395
+ if (usb_serial_jtag_is_connected ()) {
396
+ tx_timeout_ms = requested_tx_timeout_ms;
397
+ } else {
398
+ isConnected = false ;
399
+ }
345
400
if (xSemaphoreTake (tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){
346
401
return ;
347
402
}
348
403
UBaseType_t uxItemsWaiting = 0 ;
349
404
vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
350
405
if (uxItemsWaiting){
351
406
// Now trigger the ISR to read data from the ring buffer.
352
- usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
407
+ usb_serial_jtag_ll_txfifo_flush ();
408
+ if (isConnected) usb_serial_jtag_ll_ena_intr_mask (USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
353
409
}
354
- while (uxItemsWaiting){
410
+ uint8_t tries = 3 ;
411
+ while (tries && uxItemsWaiting){
355
412
delay (5 );
413
+ UBaseType_t lastUxItemsWaiting = uxItemsWaiting;
356
414
vRingbufferGetInfo (tx_ring_buf, NULL , NULL , NULL , NULL , &uxItemsWaiting);
415
+ if (lastUxItemsWaiting == uxItemsWaiting) tries--;
416
+ }
417
+ if (tries == 0 ) { // CDC isn't connected anymore...
418
+ isConnected = false ;
419
+ flushTXBuffer ();
357
420
}
358
421
xSemaphoreGive (tx_lock);
359
422
}
0 commit comments