Skip to content

Commit a971748

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 80046b1 commit a971748

File tree

1 file changed

+69
-60
lines changed
  • components/esp_driver_uart/src

1 file changed

+69
-60
lines changed

components/esp_driver_uart/src/uart.c

Lines changed: 69 additions & 60 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 {
@@ -1636,37 +1627,57 @@ static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size, bool
16361627
#endif
16371628
p_uart_obj[uart_num]->coll_det_flg = false;
16381629
if (p_uart_obj[uart_num]->tx_buf_size > 0) {
1639-
int offset = 0;
1640-
uart_tx_data_t evt;
1641-
evt.tx_data.size = size;
1642-
evt.tx_data.brk_len = brk_len;
1643-
if (brk_en) {
1644-
evt.type = UART_DATA_BREAK;
1645-
} else {
1646-
evt.type = UART_DATA;
1647-
}
1648-
UBaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void *) &evt, sizeof(uart_tx_data_t), ticks_to_wait);
1649-
if (res != pdTRUE) {
1650-
bytes_transmitted = -1;
1651-
goto unlock_and_return;
1652-
1653-
}
1630+
const char *offset = src;
16541631
while (size > 0) {
1632+
1633+
size_t acquire_size = size + sizeof(uart_tx_data_t); // Optimally, fit all data plus description in one round
1634+
1635+
// Fall back if not enough space in ring buffer
16551636
size_t free_size = xRingbufferGetCurFreeSize(p_uart_obj[uart_num]->tx_ring_buf);
1656-
size_t send_size = MIN(size, free_size);
1657-
if (send_size > 0) {
1658-
res = xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void *)(src + offset), send_size, ticks_to_wait);
1659-
if (res != pdTRUE) {
1660-
if (bytes_transmitted == 0) {
1661-
bytes_transmitted = -1;
1662-
}
1663-
goto unlock_and_return;
1664-
}
1665-
size -= send_size;
1666-
offset += send_size;
1667-
bytes_transmitted += send_size;
1668-
uart_enable_tx_intr(uart_num, 1, UART_THRESHOLD_NUM(uart_num, UART_EMPTY_THRESH_DEFAULT));
1637+
1638+
// If there is enough space for data description and 'some' data, send as much as possible in one round that
1639+
// is likely to fit in the ring buffer without blocking
1640+
if (free_size > sizeof(uart_tx_data_t)) {
1641+
acquire_size = MIN(acquire_size, free_size);
16691642
}
1643+
1644+
// Limit an item to a quarter of the ring buffer size, if we allow a full item size the ring buffer
1645+
// needs to completely empty before we can send another large item, which can cause stalls.
1646+
// also makes sure that we dont try to add an item larger than the ring buffer itself
1647+
acquire_size = MIN(acquire_size, xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) / 4);
1648+
1649+
// Accuire space for data description and data chunk, this gives a strong
1650+
// guarantee that data chunk can be copied in one go
1651+
uart_tx_data_t *tx_data = NULL;
1652+
BaseType_t res = xRingbufferSendAcquire(p_uart_obj[uart_num]->tx_ring_buf, (void **)&tx_data, acquire_size, ticks_to_wait);
1653+
if (res != pdTRUE) {
1654+
break; // Timeout
1655+
}
1656+
1657+
size_t send_size = acquire_size - sizeof(uart_tx_data_t);
1658+
1659+
// Check if this is the last chunk and we need to attach break
1660+
bool is_last_chunk = (send_size >= size);
1661+
1662+
*tx_data = (uart_tx_data_t) {
1663+
.tx_data = {
1664+
.size = send_size,
1665+
.brk_len = (is_last_chunk && brk_en) ? brk_len : 0
1666+
},
1667+
.type = (is_last_chunk && brk_en) ? UART_DATA_BREAK : UART_DATA,
1668+
};
1669+
1670+
memcpy(tx_data->tx_data.data, offset, send_size);
1671+
xRingbufferSendComplete(p_uart_obj[uart_num]->tx_ring_buf, tx_data);
1672+
1673+
size -= send_size;
1674+
offset += send_size;
1675+
bytes_transmitted += send_size;
1676+
}
1677+
1678+
// If at least some data has been sent to the ring buffer, enable TX interrupt to start sending
1679+
if (bytes_transmitted > 0) {
1680+
uart_enable_tx_intr(uart_num, 1, UART_THRESHOLD_NUM(uart_num, UART_EMPTY_THRESH_DEFAULT));
16701681
}
16711682
} else {
16721683
while (size) {
@@ -1681,10 +1692,7 @@ static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size, bool
16811692
src += sent;
16821693
bytes_transmitted += sent;
16831694
} else {
1684-
if (bytes_transmitted == 0) {
1685-
bytes_transmitted = -1;
1686-
}
1687-
goto unlock_and_return;
1695+
break;
16881696
}
16891697
}
16901698
if (brk_en) {
@@ -1694,20 +1702,22 @@ static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size, bool
16941702
uart_hal_ena_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_DONE);
16951703
UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
16961704
if (pdFALSE == xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, ticks_to_wait)) {
1697-
xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem);
16981705
uart_hal_disable_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_BRK_DONE);
1699-
bytes_transmitted = -1;
1700-
goto unlock_and_return;
1706+
bytes_transmitted = -1; // Indicate failure due to timeout waiting for break to complete
17011707
}
17021708
}
17031709
xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem);
17041710
}
17051711

1706-
unlock_and_return:
17071712
#if PROTECT_APB
17081713
esp_pm_lock_release(p_uart_obj[uart_num]->pm_lock);
17091714
#endif
17101715
xSemaphoreGive(p_uart_obj[uart_num]->tx_mux);
1716+
1717+
// If no bytes were transmitted due to timeout, return -1 to indicate failure
1718+
if (bytes_transmitted == 0) {
1719+
return -1;
1720+
}
17111721
return bytes_transmitted;
17121722
}
17131723

@@ -2059,7 +2069,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b
20592069
p_uart_obj[uart_num]->event_queue_size = event_queue_size;
20602070
p_uart_obj[uart_num]->tx_ptr = NULL;
20612071
p_uart_obj[uart_num]->tx_head = NULL;
2062-
p_uart_obj[uart_num]->trans_total_remaining_len = 0;
20632072
p_uart_obj[uart_num]->trans_chunk_remaining_len = 0;
20642073
p_uart_obj[uart_num]->tx_brk_flg = 0;
20652074
p_uart_obj[uart_num]->tx_brk_len = 0;

0 commit comments

Comments
 (0)