fix(usb_serial_jtag): Fix issue that buffer seems not flush when TX buffer is full and flush slow,

Closes https://github.com/espressif/esp-idf/issues/12628
This commit is contained in:
Cao Sen Miao 2023-11-28 18:50:21 +08:00
parent bf15a29e18
commit 8b2b9140ac

View File

@ -6,6 +6,7 @@
#include <string.h>
#include <stdbool.h>
#include <stdatomic.h>
#include "esp_log.h"
#include "hal/usb_serial_jtag_ll.h"
#include "hal/usb_fsls_phy_ll.h"
@ -25,12 +26,19 @@
#define USJ_RCC_ATOMIC()
#endif
typedef enum {
FIFO_IDLE = 0, /*!< Indicates the fifo is in idle state */
FIFO_BUSY = 1, /*!< Indicates the fifo is in busy state */
} fifo_status_t;
// The hardware buffer max size is 64
#define USB_SER_JTAG_ENDP_SIZE (64)
#define USB_SER_JTAG_RX_MAX_SIZE (64)
typedef struct{
intr_handle_t intr_handle; /*!< USB-SERIAL-JTAG interrupt handler */
portMUX_TYPE spinlock; /*!< Spinlock for usb_serial_jtag */
_Atomic fifo_status_t fifo_status; /*!< Record the status of fifo */
// RX parameters
RingbufHandle_t rx_ring_buf; /*!< RX ring buffer handler */
@ -67,7 +75,7 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
// If the hardware fifo is available, write in it. Otherwise, do nothing.
if (usb_serial_jtag_ll_txfifo_writable() == 1) {
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
size_t queued_size;
uint8_t *queued_buff = NULL;
bool is_stashed_data = false;
@ -91,7 +99,10 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
// On ringbuffer wrap-around the size can be 0 even though the buffer returned is not NULL
if (queued_size > 0) {
portENTER_CRITICAL_ISR(&p_usb_serial_jtag_obj->spinlock);
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_BUSY);
uint32_t sent_size = usb_serial_jtag_write_and_flush(queued_buff, queued_size);
portEXIT_CRITICAL_ISR(&p_usb_serial_jtag_obj->spinlock);
if (sent_size < queued_size) {
// Not all bytes could be sent at once; stash the unwritten bytes in a tx buffer
@ -108,7 +119,6 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
if (is_stashed_data == false) {
vRingbufferReturnItemFromISR(p_usb_serial_jtag_obj->tx_ring_buf, queued_buff, &xTaskWoken);
}
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
} else {
// The last transmit may have sent a full EP worth of data. The host will interpret
// this as a transaction that hasn't finished yet and keep the data in its internal
@ -119,6 +129,7 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
// flush will not by itself cause this ISR to be called again.
}
} else {
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
}
@ -147,6 +158,7 @@ esp_err_t usb_serial_jtag_driver_install(usb_serial_jtag_driver_config_t *usb_se
p_usb_serial_jtag_obj->rx_buf_size = usb_serial_jtag_config->rx_buffer_size;
p_usb_serial_jtag_obj->tx_buf_size = usb_serial_jtag_config->tx_buffer_size;
p_usb_serial_jtag_obj->tx_stash_cnt = 0;
p_usb_serial_jtag_obj->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
if (p_usb_serial_jtag_obj == NULL) {
ESP_LOGE(USB_SERIAL_JTAG_TAG, "memory allocate error");
err = ESP_ERR_NO_MEM;
@ -171,6 +183,7 @@ esp_err_t usb_serial_jtag_driver_install(usb_serial_jtag_driver_config_t *usb_se
USJ_RCC_ATOMIC() {
usb_serial_jtag_ll_enable_bus_clock(true);
}
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
// Configure PHY
usb_fsls_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG);
@ -220,10 +233,22 @@ int usb_serial_jtag_write_bytes(const void* src, size_t size, TickType_t ticks_t
ESP_RETURN_ON_FALSE(src != NULL, ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "Invalid buffer pointer.");
ESP_RETURN_ON_FALSE(p_usb_serial_jtag_obj != NULL, ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "The driver hasn't been initialized");
size_t sent_data = 0;
BaseType_t result = pdTRUE;
const uint8_t *buff = (const uint8_t *)src;
if (p_usb_serial_jtag_obj->fifo_status == FIFO_IDLE) {
portENTER_CRITICAL(&p_usb_serial_jtag_obj->spinlock);
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_BUSY);
sent_data = usb_serial_jtag_write_and_flush(src, size);
portEXIT_CRITICAL(&p_usb_serial_jtag_obj->spinlock);
}
// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
BaseType_t result = xRingbufferSend(p_usb_serial_jtag_obj->tx_ring_buf, (void*) (buff), size, ticks_to_wait);
// Now trigger the ISR to read data from the ring buffer.
if (size - sent_data > 0) {
result = xRingbufferSend(p_usb_serial_jtag_obj->tx_ring_buf, (void*) (buff+sent_data), size-sent_data, ticks_to_wait);
} else {
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
}
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
return (result == pdFALSE) ? 0 : size;
}