Skip to content

Commit cac882e

Browse files
author
Jiang Jiang Jian
committed
Merge branch 'bugfix/usb_serial_jtag_simplify_v5.1' into 'release/v5.1'
usb-serial-jtag driver simplification (backport v5.1) See merge request espressif/esp-idf!33909
2 parents 75e3868 + 5b43155 commit cac882e

File tree

4 files changed

+83
-110
lines changed

4 files changed

+83
-110
lines changed

components/driver/usb_serial_jtag/usb_serial_jtag.c

Lines changed: 69 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -15,122 +15,107 @@
1515
#include "esp_intr_alloc.h"
1616
#include "driver/usb_serial_jtag.h"
1717
#include "soc/periph_defs.h"
18+
#include "soc/soc_caps.h"
1819
#include "esp_check.h"
20+
#include "esp_private/periph_ctrl.h"
1921

20-
typedef enum {
21-
FIFO_IDLE = 0, /*!< Indicates the fifo is in idle state */
22-
FIFO_BUSY = 1, /*!< Indicates the fifo is in busy state */
23-
} fifo_status_t;
22+
/*
23+
Note: Before you add a workaround for an issue in this driver, please please try
24+
to figure out the actual root cause first. The USB-serial-JTAG is a simple device,
25+
it shouldn't need anything more than a simple, straightforward driver.
26+
*/
2427

25-
// The hardware buffer max size is 64
28+
// The hardware buffer max size is 64, both for RX and TX.
2629
#define USB_SER_JTAG_ENDP_SIZE (64)
27-
#define USB_SER_JTAG_RX_MAX_SIZE (64)
30+
#define USB_SER_JTAG_RX_MAX_SIZE (USB_SER_JTAG_ENDP_SIZE)
2831

2932
typedef struct{
3033
intr_handle_t intr_handle; /*!< USB-SERIAL-JTAG interrupt handler */
31-
portMUX_TYPE spinlock; /*!< Spinlock for usb_serial_jtag */
32-
_Atomic fifo_status_t fifo_status; /*!< Record the status of fifo */
3334

3435
// RX parameters
3536
RingbufHandle_t rx_ring_buf; /*!< RX ring buffer handler */
36-
uint32_t rx_buf_size; /*!< TX buffer size */
37-
uint8_t rx_data_buf[USB_SER_JTAG_ENDP_SIZE]; /*!< Data buffer to stash FIFO data */
3837

3938
// TX parameters
40-
uint32_t tx_buf_size; /*!< TX buffer size */
4139
RingbufHandle_t tx_ring_buf; /*!< TX ring buffer handler */
42-
uint8_t tx_data_buf[USB_SER_JTAG_ENDP_SIZE]; /*!< Data buffer to stash TX FIFO data */
40+
uint8_t tx_stash_buf[USB_SER_JTAG_ENDP_SIZE]; /*!< Data buffer to stash TX FIFO data */
4341
size_t tx_stash_cnt; /*!< Number of stashed TX FIFO bytes */
4442
} usb_serial_jtag_obj_t;
4543

4644
static usb_serial_jtag_obj_t *p_usb_serial_jtag_obj = NULL;
4745

4846
static const char* USB_SERIAL_JTAG_TAG = "usb_serial_jtag";
4947

50-
static size_t usb_serial_jtag_write_and_flush(const uint8_t *buf, uint32_t wr_len)
48+
static void usb_serial_jtag_isr_handler_default(void *arg)
5149
{
52-
size_t size = usb_serial_jtag_ll_write_txfifo(buf, wr_len);
53-
usb_serial_jtag_ll_txfifo_flush();
54-
return size;
55-
}
56-
57-
static void usb_serial_jtag_isr_handler_default(void *arg) {
58-
portBASE_TYPE xTaskWoken = 0;
50+
BaseType_t xTaskWoken = 0;
5951
uint32_t usbjtag_intr_status = 0;
6052
usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask();
6153

6254
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
55+
//Clear interrupt so we won't be called until the next transfer finishes.
56+
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
57+
6358
// Interrupt tells us the host picked up the data we sent.
6459
// If we have more data, we can put it in the buffer and the host will pick that up next.
65-
// Send data in isr.
66-
// If the hardware fifo is available, write in it. Otherwise, do nothing.
60+
// We expect the TX FIFO to be writable for this. If it's not, somehow someone else
61+
// (ROM print routines?) have snuck in a full buffer before we got here. In that case,
62+
// we simply ignore the interrupt, a new one will come if the buffer is empty again.
6763
if (usb_serial_jtag_ll_txfifo_writable() == 1) {
68-
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
69-
64+
// Retrieve data from either the stash buffer or, if that's empty, from the ring buffer.
7065
size_t queued_size;
71-
uint8_t *queued_buff = NULL;
72-
bool is_stashed_data = false;
66+
uint8_t *queued_buf = NULL;
7367
if (p_usb_serial_jtag_obj->tx_stash_cnt != 0) {
7468
// Send stashed tx bytes before reading bytes from ring buffer
75-
queued_buff = p_usb_serial_jtag_obj->tx_data_buf;
69+
queued_buf = p_usb_serial_jtag_obj->tx_stash_buf;
7670
queued_size = p_usb_serial_jtag_obj->tx_stash_cnt;
77-
is_stashed_data = true;
7871
} else {
7972
// Max 64 data payload size in a single EndPoint
80-
queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(p_usb_serial_jtag_obj->tx_ring_buf, &queued_size, USB_SER_JTAG_ENDP_SIZE);
73+
queued_buf = (uint8_t *)xRingbufferReceiveUpToFromISR(p_usb_serial_jtag_obj->tx_ring_buf, &queued_size, USB_SER_JTAG_ENDP_SIZE);
8174
}
8275

83-
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
84-
85-
if (queued_buff != NULL) {
86-
87-
// Although tx_queued_bytes may be larger than 0, we may have
88-
// interrupted before xRingbufferSend() was called.
89-
// Copy the queued buffer into the TX FIFO
90-
91-
// On ringbuffer wrap-around the size can be 0 even though the buffer returned is not NULL
92-
if (queued_size > 0) {
93-
portENTER_CRITICAL_ISR(&p_usb_serial_jtag_obj->spinlock);
94-
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_BUSY);
95-
uint32_t sent_size = usb_serial_jtag_write_and_flush(queued_buff, queued_size);
96-
portEXIT_CRITICAL_ISR(&p_usb_serial_jtag_obj->spinlock);
97-
98-
if (sent_size < queued_size) {
99-
// Not all bytes could be sent at once; stash the unwritten bytes in a tx buffer
100-
// stash_size will not larger than USB_SER_JTAG_ENDP_SIZE because queued_size is got from xRingbufferReceiveUpToFromISR
101-
size_t stash_size = queued_size - sent_size;
102-
memcpy(p_usb_serial_jtag_obj->tx_data_buf, &queued_buff[sent_size], stash_size);
103-
p_usb_serial_jtag_obj->tx_stash_cnt = stash_size;
104-
} else {
105-
p_usb_serial_jtag_obj->tx_stash_cnt = 0;
106-
// assert if sent_size is larger than queued_size.
107-
assert(sent_size <= queued_size);
108-
}
76+
if (queued_buf != NULL && queued_size > 0) {
77+
// We have some data to send. Send it.
78+
uint32_t sent_size = usb_serial_jtag_ll_write_txfifo(queued_buf, queued_size);
79+
usb_serial_jtag_ll_txfifo_flush();
80+
81+
// Check if we were able to send everything.
82+
if (sent_size < queued_size) {
83+
// Not all bytes could be sent at once; stash the unwritten bytes in a buffer
84+
// This will happen if e.g. the rom output functions manage to sneak a few bytes into the
85+
// TX FIFO before this interrupt triggers. Note stash_size will not larger than
86+
// USB_SER_JTAG_ENDP_SIZE because queued_size is obtained from xRingbufferReceiveUpToFromISR.
87+
size_t stash_size = queued_size - sent_size;
88+
memcpy(p_usb_serial_jtag_obj->tx_stash_buf, &queued_buf[sent_size], stash_size);
89+
p_usb_serial_jtag_obj->tx_stash_cnt = stash_size;
90+
} else {
91+
p_usb_serial_jtag_obj->tx_stash_cnt = 0;
10992
}
110-
if (is_stashed_data == false) {
111-
vRingbufferReturnItemFromISR(p_usb_serial_jtag_obj->tx_ring_buf, queued_buff, &xTaskWoken);
93+
// Return the buffer if we got it from the ring buffer.
94+
if (queued_buf != p_usb_serial_jtag_obj->tx_stash_buf) {
95+
vRingbufferReturnItemFromISR(p_usb_serial_jtag_obj->tx_ring_buf, queued_buf, &xTaskWoken);
11296
}
11397
} else {
98+
// No data to send.
11499
// The last transmit may have sent a full EP worth of data. The host will interpret
115100
// this as a transaction that hasn't finished yet and keep the data in its internal
116101
// buffers rather than releasing it to the program listening on the CDC serial port.
117102
// We need to flush again in order to send a 0-byte packet that ends the transaction.
118103
usb_serial_jtag_ll_txfifo_flush();
119-
// Note that since this doesn't re-enable USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY, the
120-
// flush will not by itself cause this ISR to be called again.
104+
105+
// We will also disable the interrupt as for now there's no need to handle the
106+
// TX interrupt again. We'll re-enable this externally if we need data sent.
107+
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
121108
}
122-
} else {
123-
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
124-
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
125109
}
126110
}
127111

128112
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT) {
129-
// read rx buffer(max length is 64), and send avaliable data to ringbuffer.
130-
// Ensure the rx buffer size is larger than RX_MAX_SIZE.
113+
// Acknowledge interrupt
131114
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
132-
uint32_t rx_fifo_len = usb_serial_jtag_ll_read_rxfifo(p_usb_serial_jtag_obj->rx_data_buf, USB_SER_JTAG_RX_MAX_SIZE);
133-
xRingbufferSendFromISR(p_usb_serial_jtag_obj->rx_ring_buf, p_usb_serial_jtag_obj->rx_data_buf, rx_fifo_len, &xTaskWoken);
115+
// Read RX FIFO and send available data to ringbuffer.
116+
uint8_t buf[USB_SER_JTAG_RX_MAX_SIZE];
117+
uint32_t rx_fifo_len = usb_serial_jtag_ll_read_rxfifo(buf, USB_SER_JTAG_RX_MAX_SIZE);
118+
xRingbufferSendFromISR(p_usb_serial_jtag_obj->rx_ring_buf, buf, rx_fifo_len, &xTaskWoken);
134119
}
135120

136121
if (xTaskWoken == pdTRUE) {
@@ -145,18 +130,16 @@ esp_err_t usb_serial_jtag_driver_install(usb_serial_jtag_driver_config_t *usb_se
145130
ESP_RETURN_ON_FALSE((usb_serial_jtag_config->rx_buffer_size > 0), ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "RX buffer is not prepared");
146131
ESP_RETURN_ON_FALSE((usb_serial_jtag_config->rx_buffer_size > USB_SER_JTAG_RX_MAX_SIZE), ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "RX buffer prepared is so small, should larger than 64");
147132
ESP_RETURN_ON_FALSE((usb_serial_jtag_config->tx_buffer_size > 0), ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "TX buffer is not prepared");
148-
p_usb_serial_jtag_obj = (usb_serial_jtag_obj_t*) heap_caps_calloc(1, sizeof(usb_serial_jtag_obj_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
149-
p_usb_serial_jtag_obj->rx_buf_size = usb_serial_jtag_config->rx_buffer_size;
150-
p_usb_serial_jtag_obj->tx_buf_size = usb_serial_jtag_config->tx_buffer_size;
151-
p_usb_serial_jtag_obj->tx_stash_cnt = 0;
152-
p_usb_serial_jtag_obj->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
133+
p_usb_serial_jtag_obj = (usb_serial_jtag_obj_t*) heap_caps_calloc(1, sizeof(usb_serial_jtag_obj_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
153134
if (p_usb_serial_jtag_obj == NULL) {
154135
ESP_LOGE(USB_SERIAL_JTAG_TAG, "memory allocate error");
155-
err = ESP_ERR_NO_MEM;
156-
goto _exit;
136+
// no `goto _exit` here as there's nothing to clean up and that would make the uninstall
137+
// routine unhappy.
138+
return ESP_ERR_NO_MEM;
157139
}
140+
p_usb_serial_jtag_obj->tx_stash_cnt = 0;
158141

159-
p_usb_serial_jtag_obj->rx_ring_buf = xRingbufferCreate(p_usb_serial_jtag_obj->rx_buf_size, RINGBUF_TYPE_BYTEBUF);
142+
p_usb_serial_jtag_obj->rx_ring_buf = xRingbufferCreate(usb_serial_jtag_config->rx_buffer_size, RINGBUF_TYPE_BYTEBUF);
160143
if (p_usb_serial_jtag_obj->rx_ring_buf == NULL) {
161144
ESP_LOGE(USB_SERIAL_JTAG_TAG, "ringbuffer create error");
162145
err = ESP_ERR_NO_MEM;
@@ -172,7 +155,6 @@ esp_err_t usb_serial_jtag_driver_install(usb_serial_jtag_driver_config_t *usb_se
172155

173156
// Enable USB-Serial-JTAG peripheral module clock
174157
usb_serial_jtag_ll_enable_bus_clock(true);
175-
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
176158

177159
// Configure PHY
178160
#if USB_SERIAL_JTAG_LL_EXT_PHY_SUPPORTED
@@ -231,37 +213,29 @@ int usb_serial_jtag_write_bytes(const void* src, size_t size, TickType_t ticks_t
231213
ESP_RETURN_ON_FALSE(src != NULL, ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "Invalid buffer pointer.");
232214
ESP_RETURN_ON_FALSE(p_usb_serial_jtag_obj != NULL, ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "The driver hasn't been initialized");
233215

234-
size_t sent_data = 0;
235216
BaseType_t result = pdTRUE;
236-
const uint8_t *buff = (const uint8_t *)src;
237-
if (p_usb_serial_jtag_obj->fifo_status == FIFO_IDLE) {
238-
portENTER_CRITICAL(&p_usb_serial_jtag_obj->spinlock);
239-
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_BUSY);
240-
sent_data = usb_serial_jtag_write_and_flush(src, size);
241-
portEXIT_CRITICAL(&p_usb_serial_jtag_obj->spinlock);
242-
}
243-
244-
// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
245-
if (size - sent_data > 0) {
246-
result = xRingbufferSend(p_usb_serial_jtag_obj->tx_ring_buf, (void*) (buff+sent_data), size-sent_data, ticks_to_wait);
247-
} else {
248-
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
249-
}
217+
result = xRingbufferSend(p_usb_serial_jtag_obj->tx_ring_buf, (void*)src, size, ticks_to_wait);
218+
// Re-enable the TX interrupt. If this was disabled, this will immediately trigger the ISR
219+
// and send the things we just put in the ringbuffer.
250220
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
251221
return (result == pdFALSE) ? 0 : size;
252222
}
253223

224+
//Note that this is also called when usb_serial_jtag_driver_install errors out and as such should
225+
//work on a half-initialized driver as well.
254226
esp_err_t usb_serial_jtag_driver_uninstall(void)
255227
{
256-
if(p_usb_serial_jtag_obj == NULL) {
257-
ESP_LOGI(USB_SERIAL_JTAG_TAG, "ALREADY NULL");
228+
if (p_usb_serial_jtag_obj == NULL) {
229+
ESP_LOGE(USB_SERIAL_JTAG_TAG, "uninstall without install called");
258230
return ESP_OK;
259231
}
260232

261-
/* Not disable the module clock and usb_pad_enable here since the USJ stdout might still depends on it. */
262-
//Disable tx/rx interrupt.
233+
/* Don't disable the module clock and usb_pad_enable here since the USJ stdout might still depends on it. */
234+
263235
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
264-
esp_intr_free(p_usb_serial_jtag_obj->intr_handle);
236+
if (p_usb_serial_jtag_obj->intr_handle) {
237+
esp_intr_free(p_usb_serial_jtag_obj->intr_handle);
238+
}
265239

266240
if(p_usb_serial_jtag_obj->rx_ring_buf) {
267241
vRingbufferDelete(p_usb_serial_jtag_obj->rx_ring_buf);

components/soc/esp32c6/include/soc/usb_serial_jtag_struct.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -23,8 +23,7 @@ typedef union {
2323
* can check USB_SERIAL_JTAG_OUT_EP1_WR_ADDR USB_SERIAL_JTAG_OUT_EP0_RD_ADDR to know
2424
* how many data is received, then read data from UART Rx FIFO.
2525
*/
26-
uint32_t rdwr_byte:8;
27-
uint32_t reserved_8:24;
26+
uint32_t rdwr_byte:32;
2827
};
2928
uint32_t val;
3029
} usb_serial_jtag_ep1_reg_t;
@@ -131,7 +130,7 @@ typedef union {
131130
*/
132131
uint32_t test_enable:1;
133132
/** test_usb_oe : R/W; bitpos: [1]; default: 0;
134-
* USB pad oen in test
133+
* USB pad output enable in test
135134
*/
136135
uint32_t test_usb_oe:1;
137136
/** test_tx_dp : R/W; bitpos: [2]; default: 0;
@@ -290,7 +289,7 @@ typedef union {
290289
*/
291290
uint32_t serial_out_afifo_reset_rd:1;
292291
/** serial_out_afifo_rempty : RO; bitpos: [4]; default: 1;
293-
* CDC_ACM OUTOUT async FIFO empty signal in read clock domain.
292+
* CDC_ACM OUTPUT async FIFO empty signal in read clock domain.
294293
*/
295294
uint32_t serial_out_afifo_rempty:1;
296295
/** serial_in_afifo_wfull : RO; bitpos: [5]; default: 0;

components/soc/esp32h2/include/soc/usb_serial_jtag_struct.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -23,8 +23,7 @@ typedef union {
2323
* can check USB_SERIAL_JTAG_OUT_EP1_WR_ADDR USB_SERIAL_JTAG_OUT_EP0_RD_ADDR to know
2424
* how many data is received, then read data from UART Rx FIFO.
2525
*/
26-
uint32_t rdwr_byte:8;
27-
uint32_t reserved_8:24;
26+
uint32_t rdwr_byte:32;
2827
};
2928
uint32_t val;
3029
} usb_serial_jtag_ep1_reg_t;
@@ -131,7 +130,7 @@ typedef union {
131130
*/
132131
uint32_t test_enable:1;
133132
/** test_usb_oe : R/W; bitpos: [1]; default: 0;
134-
* USB pad oen in test
133+
* USB pad output enable in test
135134
*/
136135
uint32_t test_usb_oe:1;
137136
/** test_tx_dp : R/W; bitpos: [2]; default: 0;
@@ -290,7 +289,7 @@ typedef union {
290289
*/
291290
uint32_t serial_out_afifo_reset_rd:1;
292291
/** serial_out_afifo_rempty : RO; bitpos: [4]; default: 1;
293-
* CDC_ACM OUTOUT async FIFO empty signal in read clock domain.
292+
* CDC_ACM OUTPUT async FIFO empty signal in read clock domain.
294293
*/
295294
uint32_t serial_out_afifo_rempty:1;
296295
/** serial_in_afifo_wfull : RO; bitpos: [5]; default: 0;

components/vfs/vfs_usb_serial_jtag.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -120,6 +120,11 @@ static void usb_serial_jtag_tx_char(int fd, int c)
120120
do {
121121
if (usb_serial_jtag_ll_txfifo_writable()) {
122122
usb_serial_jtag_ll_write_txfifo(&cc, 1);
123+
if (c == '\n') {
124+
//Make sure line doesn't linger in fifo
125+
usb_serial_jtag_ll_txfifo_flush();
126+
}
127+
//update time of last successful tx to now.
123128
s_ctx.last_tx_ts = esp_timer_get_time();
124129
break;
125130
}
@@ -154,10 +159,6 @@ static ssize_t usb_serial_jtag_write(int fd, const void * data, size_t size)
154159
}
155160
}
156161
s_ctx.tx_func(fd, c);
157-
if (c == '\n') {
158-
//Make sure line doesn't linger in fifo
159-
usb_serial_jtag_ll_txfifo_flush();
160-
}
161162
}
162163
_lock_release_recursive(&s_ctx.write_lock);
163164
return size;

0 commit comments

Comments
 (0)