@@ -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