Skip to content

Commit 474ecad

Browse files
committed
feat(esp_driver_uart): Add header and data in one chunk
This makes sure we wont break between header and data, fixes a proposed spinlock, and simplifies locking.
1 parent a6e7046 commit 474ecad

File tree

1 file changed

+57
-40
lines changed
  • components/esp_driver_uart/src

1 file changed

+57
-40
lines changed

components/esp_driver_uart/src/uart.c

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ typedef struct {
153153
bool tx_waiting_fifo; /*!< this flag indicates that some task is waiting for FIFO empty interrupt, used to send all data without any data buffer*/
154154
uint8_t *tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/
155155
uart_tx_data_t *tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/
156-
uint32_t trans_total_remaining_len; /*!< Remaining data length of the current processing transaction in TX ring buffer*/
157156
uint32_t trans_chunk_remaining_len; /*!< Remaining data length of the current processing chunk of the transaction in TX ring buffer*/
158157
uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */
159158
uint8_t tx_brk_len; /*!< TX break signal cycle length/number */
@@ -1248,56 +1247,48 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param)
12481247
//That would cause a watch_dog reset because empty interrupt happens so often.
12491248
//Although this is a loop in ISR, this loop will execute at most 128 turns.
12501249
while (tx_fifo_rem) {
1251-
if (p_uart->trans_total_remaining_len == 0 || p_uart->tx_ptr == NULL || p_uart->trans_chunk_remaining_len == 0) {
1250+
if (p_uart->tx_ptr == NULL || p_uart->trans_chunk_remaining_len == 0) {
12521251
size_t size;
12531252
p_uart->tx_head = (uart_tx_data_t *) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size);
12541253
if (p_uart->tx_head) {
1255-
//The first item is the data description
1256-
//Get the first item to get the data information
1257-
if (p_uart->trans_total_remaining_len == 0) {
1258-
p_uart->tx_ptr = NULL;
1259-
p_uart->trans_total_remaining_len = p_uart->tx_head->tx_data.size;
1260-
if (p_uart->tx_head->type == UART_DATA_BREAK) {
1261-
p_uart->tx_brk_flg = 1;
1262-
p_uart->tx_brk_len = p_uart->tx_head->tx_data.brk_len;
1263-
}
1264-
//We have saved the data description from the 1st item, return buffer.
1265-
vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken);
1266-
need_yield |= (HPTaskAwoken == pdTRUE);
1267-
} else if (p_uart->tx_ptr == NULL) {
1268-
//Update the TX item pointer, we will need this to return item to buffer.
1269-
p_uart->tx_ptr = (uint8_t *)p_uart->tx_head;
1270-
en_tx_flg = true;
1271-
p_uart->trans_chunk_remaining_len = size;
1254+
// Atomic item: header + data combined
1255+
// Extract break info if present
1256+
if (p_uart->tx_head->type == UART_DATA_BREAK) {
1257+
p_uart->tx_brk_flg = 1;
1258+
p_uart->tx_brk_len = p_uart->tx_head->tx_data.brk_len;
12721259
}
1260+
// Data starts immediately after header
1261+
p_uart->tx_ptr = p_uart->tx_head->tx_data.data;
1262+
p_uart->trans_chunk_remaining_len = p_uart->tx_head->tx_data.size;
1263+
en_tx_flg = true;
12731264
} else {
12741265
//Can not get data from ring buffer, return;
12751266
break;
12761267
}
12771268
}
1278-
if (p_uart->trans_total_remaining_len > 0 && p_uart->tx_ptr && p_uart->trans_chunk_remaining_len > 0) {
1269+
if (p_uart->tx_ptr && p_uart->trans_chunk_remaining_len > 0) {
12791270
// To fill the TX FIFO.
12801271
uint32_t send_len = uart_enable_tx_write_fifo(uart_num, (const uint8_t *) p_uart->tx_ptr,
12811272
MIN(p_uart->trans_chunk_remaining_len, tx_fifo_rem));
12821273
p_uart->tx_ptr += send_len;
1283-
p_uart->trans_total_remaining_len -= send_len;
12841274
p_uart->trans_chunk_remaining_len -= send_len;
12851275
tx_fifo_rem -= send_len;
12861276
if (p_uart->trans_chunk_remaining_len == 0) {
1287-
//Return item to ring buffer.
1277+
//Return atomic item to ring buffer.
12881278
vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken);
12891279
need_yield |= (HPTaskAwoken == pdTRUE);
12901280
p_uart->tx_head = NULL;
12911281
p_uart->tx_ptr = NULL;
12921282
//Sending item done, now we need to send break if there is a record.
12931283
//Set TX break signal after FIFO is empty
1294-
if (p_uart->trans_total_remaining_len == 0 && p_uart->tx_brk_flg == 1) {
1284+
if (p_uart->tx_brk_flg == 1) {
12951285
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_DONE);
12961286
UART_ENTER_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
12971287
uart_hal_tx_break(&(uart_context[uart_num].hal), p_uart->tx_brk_len);
12981288
uart_hal_ena_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_DONE);
12991289
UART_EXIT_CRITICAL_ISR(&(uart_context[uart_num].spinlock));
13001290
p_uart->tx_waiting_brk = 1;
1291+
p_uart->tx_brk_flg = 0;
13011292
//do not enable TX empty interrupt
13021293
en_tx_flg = false;
13031294
} else {
@@ -1634,26 +1625,51 @@ static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size, bool
16341625
#endif
16351626
p_uart_obj[uart_num]->coll_det_flg = false;
16361627
if (p_uart_obj[uart_num]->tx_buf_size > 0) {
1637-
int offset = 0;
1638-
uart_tx_data_t evt;
1639-
evt.tx_data.size = size;
1640-
evt.tx_data.brk_len = brk_len;
1641-
if (brk_en) {
1642-
evt.type = UART_DATA_BREAK;
1643-
} else {
1644-
evt.type = UART_DATA;
1645-
}
1646-
xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void *) &evt, sizeof(uart_tx_data_t), portMAX_DELAY);
1628+
const char *offset = src;
16471629
while (size > 0) {
1630+
1631+
size_t acquire_size = size + sizeof(uart_tx_data_t); // Optimally, fit all data plus description in one round
1632+
1633+
// Fall back if not enough space in ring buffer
16481634
size_t free_size = xRingbufferGetCurFreeSize(p_uart_obj[uart_num]->tx_ring_buf);
1649-
size_t send_size = MIN(size, free_size);
1650-
if (send_size > 0) {
1651-
xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void *)(src + offset), send_size, portMAX_DELAY);
1652-
size -= send_size;
1653-
offset += send_size;
1654-
uart_enable_tx_intr(uart_num, 1, UART_THRESHOLD_NUM(uart_num, UART_EMPTY_THRESH_DEFAULT));
1635+
1636+
// If there is enough space for data description and 'some' data, send as much as possible in one round that
1637+
// is likely to fit in the ring buffer without blocking
1638+
if (free_size > sizeof(uart_tx_data_t)) {
1639+
acquire_size = MIN(acquire_size, free_size);
16551640
}
1641+
1642+
// Limit an item to a quarter of the ring buffer size, if we allow a full item size the ring buffer
1643+
// needs to completely empty before we can send another large item, which can cause stalls.
1644+
// also makes sure that we dont try to add an item larger than the ring buffer itself
1645+
acquire_size = MIN(acquire_size, xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) / 4);
1646+
1647+
// Accuire space for data description and data chunk, this gives a strong
1648+
// guarantee that data chunk can be copied in one go
1649+
uart_tx_data_t *tx_data = NULL;
1650+
xRingbufferSendAcquire(p_uart_obj[uart_num]->tx_ring_buf, (void **)&tx_data, acquire_size, portMAX_DELAY);
1651+
1652+
size_t send_size = acquire_size - sizeof(uart_tx_data_t);
1653+
1654+
// Check if this is the last chunk and we need to attach break
1655+
bool is_last_chunk = (send_size >= size);
1656+
1657+
*tx_data = (uart_tx_data_t) {
1658+
.tx_data = {
1659+
.size = send_size,
1660+
.brk_len = (is_last_chunk && brk_en) ? brk_len : 0
1661+
},
1662+
.type = (is_last_chunk && brk_en) ? UART_DATA_BREAK : UART_DATA,
1663+
};
1664+
1665+
memcpy(tx_data->tx_data.data, offset, send_size);
1666+
xRingbufferSendComplete(p_uart_obj[uart_num]->tx_ring_buf, tx_data);
1667+
1668+
size -= send_size;
1669+
offset += send_size;
16561670
}
1671+
1672+
uart_enable_tx_intr(uart_num, 1, UART_THRESHOLD_NUM(uart_num, UART_EMPTY_THRESH_DEFAULT));
16571673
} else {
16581674
while (size) {
16591675
//semaphore for tx_fifo available
@@ -1665,6 +1681,8 @@ static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size, bool
16651681
}
16661682
size -= sent;
16671683
src += sent;
1684+
} else {
1685+
break;
16681686
}
16691687
}
16701688
if (brk_en) {
@@ -2024,7 +2042,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b
20242042
p_uart_obj[uart_num]->event_queue_size = event_queue_size;
20252043
p_uart_obj[uart_num]->tx_ptr = NULL;
20262044
p_uart_obj[uart_num]->tx_head = NULL;
2027-
p_uart_obj[uart_num]->trans_total_remaining_len = 0;
20282045
p_uart_obj[uart_num]->trans_chunk_remaining_len = 0;
20292046
p_uart_obj[uart_num]->tx_brk_flg = 0;
20302047
p_uart_obj[uart_num]->tx_brk_len = 0;

0 commit comments

Comments
 (0)