fix: potential blocking code when call uart_wait_tx_done()
1. there is a risk of ticks_end overflow, if xTaskGetTickCount() plus ticks_to_wait is bigger than portMAX_DELAY
2. potential blocking code on waiting for tx fifo done. It usually occurs at esp uart uses flow control, and the other side of uart not.
@@ -257,7 +257,18 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait)
uint32_t baudrate;
uint32_t byte_delay_us = 0;
BaseType_t res;
+ portTickType ticks_cur;
+ portTickType ticks_start = xTaskGetTickCount();
portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait;
+ /**
+ * Considering the overflow of the ticks_end and the ticks_cur (xTaskGetTickCount()),
+ * the possible tick timestamp is as follows:
+ * (one start tick timestamp, two end tick timestamps, four current tick timestamps)
+ *
+ * ticks: 0 0xFFFFFFFF
+ * |_______._______._______._______._______._______._______._______|
+ * cur1 end1 cur2 start cur3 end2 cur4
+ */
// Take tx_mux
res = xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)ticks_to_wait);
@@ -273,10 +284,22 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait)
uart_get_baudrate(uart_num, &baudrate);
byte_delay_us = (uint32_t)(10000000 / baudrate); // (1/baudrate)*10*1000_000 us
- ticks_to_wait = ticks_end - xTaskGetTickCount();
+ ticks_cur = xTaskGetTickCount();
+ if (ticks_start <= ticks_cur) {
+ ticks_to_wait = ticks_to_wait - (ticks_cur - ticks_start);
+ } else {
+ ticks_to_wait = ticks_to_wait - (portMAX_DELAY - ticks_start + ticks_cur);
+ }
// wait for tx done sem.
if (pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, ticks_to_wait)) {
while (1) {
+ ticks_cur = xTaskGetTickCount();
+ bool end1_timeout = (ticks_end < ticks_start && ticks_cur < ticks_start && ticks_cur > ticks_end);
+ bool end2_timeout = (ticks_start < ticks_end && (ticks_cur < ticks_start || ticks_end < ticks_cur));
+ if (end1_timeout || end2_timeout) {
+ xSemaphoreGive(p_uart_obj[uart_num]->tx_mux);
+ return ESP_ERR_TIMEOUT;
+ }
if (UART[uart_num]->status.txfifo_cnt == 0) {
ets_delay_us(byte_delay_us); // Delay one byte time to guarantee transmission completion
break;
