Skip to content

Commit 2420b31

Browse files
saikumar-mandajifpistm
authored andcommitted
fix(Serial): Uart::write() must not spin when begin() failed
If Serial.begin(baud) fails during hardware init (e.g. an LPUART clocked from LSE can't reach the requested baud rate), uart_init() returns false and _ready is set to false -- but Uart::write() never checks _ready before entering its transmit path. Uart::write(const uint8_t*, size_t) contains: while (!availableForWrite()) { // nop, the interrupt handler will free up space for us } Since the hardware was never actually brought up, the TX interrupt this loop waits on never fires, so once the 63-byte TX ring buffer fills up (a few Serial.print() calls after a failed begin()), this spins forever and the MCU deadlocks permanently. Fix: return 0 immediately if !_ready, before touching the buffer or entering the wait loop, using the same _ready accessor already used elsewhere in this file (see Uart::begin(), which sets it, and the existing operator bool()-style accessor in Serial.h). write(uint8_t) needs no separate guard since it already delegates to this overload. Fixes #3071
1 parent bc75051 commit 2420b31

1 file changed

Lines changed: 56 additions & 49 deletions

File tree

‎cores/arduino/Serial.cpp‎

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -583,62 +583,69 @@ void Uart::flush(uint32_t timeout)
583583

584584
size_t Uart::write(const uint8_t *buffer, size_t size)
585585
{
586-
size_t size_intermediate;
587586
size_t ret = size;
588-
size_t available = availableForWrite();
589-
size_t available_till_buffer_end = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
590-
591-
_written = true;
592-
if (isHalfDuplex()) {
593-
if (_rx_enabled) {
594-
_rx_enabled = false;
595-
uart_enable_tx(&_serial);
587+
// If begin() never brought the hardware up (e.g. the requested baud
588+
// rate can't be reached from the configured kernel clock), the TX
589+
// interrupt this function waits on below will never fire. Bail out
590+
// instead of spinning forever once the TX buffer fills up.
591+
if (!_ready) {
592+
ret = 0;
593+
} else {
594+
size_t size_intermediate;
595+
size_t available = availableForWrite();
596+
size_t available_till_buffer_end = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
597+
598+
_written = true;
599+
if (isHalfDuplex()) {
600+
if (_rx_enabled) {
601+
_rx_enabled = false;
602+
uart_enable_tx(&_serial);
603+
}
596604
}
597-
}
598605

599-
// If the output buffer is full, there's nothing for it other than to
600-
// wait for the interrupt handler to free space
601-
while (!availableForWrite()) {
602-
// nop, the interrupt handler will free up space for us
603-
}
606+
// If the output buffer is full, there's nothing for it other than to
607+
// wait for the interrupt handler to free space
608+
while (!availableForWrite()) {
609+
// nop, the interrupt handler will free up space for us
610+
}
604611

605-
// HAL doesn't manage rollover, so split transfer till end of TX buffer
606-
// Also, split transfer according to available space in buffer
607-
while ((size > available_till_buffer_end) || (size > available)) {
608-
size_intermediate = min(available, available_till_buffer_end);
609-
write(buffer, size_intermediate);
610-
size -= size_intermediate;
611-
buffer += size_intermediate;
612-
available = availableForWrite();
613-
available_till_buffer_end = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
614-
}
612+
// HAL doesn't manage rollover, so split transfer till end of TX buffer
613+
// Also, split transfer according to available space in buffer
614+
while ((size > available_till_buffer_end) || (size > available)) {
615+
size_intermediate = min(available, available_till_buffer_end);
616+
write(buffer, size_intermediate);
617+
size -= size_intermediate;
618+
buffer += size_intermediate;
619+
available = availableForWrite();
620+
available_till_buffer_end = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
621+
}
615622

616-
// Copy data to buffer. Take into account rollover if necessary.
617-
if (_serial.tx_head + size <= SERIAL_TX_BUFFER_SIZE) {
618-
memcpy(&_serial.tx_buff[_serial.tx_head], buffer, size);
619-
size_intermediate = size;
620-
} else {
621-
// memcpy till end of buffer then continue memcpy from beginning of buffer
622-
size_intermediate = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
623-
memcpy(&_serial.tx_buff[_serial.tx_head], buffer, size_intermediate);
624-
memcpy(&_serial.tx_buff[0], buffer + size_intermediate,
625-
size - size_intermediate);
626-
}
623+
// Copy data to buffer. Take into account rollover if necessary.
624+
if (_serial.tx_head + size <= SERIAL_TX_BUFFER_SIZE) {
625+
memcpy(&_serial.tx_buff[_serial.tx_head], buffer, size);
626+
size_intermediate = size;
627+
} else {
628+
// memcpy till end of buffer then continue memcpy from beginning of buffer
629+
size_intermediate = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
630+
memcpy(&_serial.tx_buff[_serial.tx_head], buffer, size_intermediate);
631+
memcpy(&_serial.tx_buff[0], buffer + size_intermediate,
632+
size - size_intermediate);
633+
}
627634

628-
// Data are copied to buffer, move head pointer accordingly
629-
_serial.tx_head = (_serial.tx_head + size) % SERIAL_TX_BUFFER_SIZE;
630-
631-
// Transfer data with HAL only is there is no TX data transfer ongoing
632-
// otherwise, data transfer will be done asynchronously from callback
633-
if (!serial_tx_active(&_serial)) {
634-
// note: tx_size correspond to size of HAL data transfer,
635-
// not the total amount of data in the buffer.
636-
// To compute size of data in buffer compare head and tail
637-
_serial.tx_size = size_intermediate;
638-
uart_attach_tx_callback(&_serial, _tx_complete_irq, size_intermediate);
635+
// Data are copied to buffer, move head pointer accordingly
636+
_serial.tx_head = (_serial.tx_head + size) % SERIAL_TX_BUFFER_SIZE;
637+
638+
// Transfer data with HAL only is there is no TX data transfer ongoing
639+
// otherwise, data transfer will be done asynchronously from callback
640+
if (!serial_tx_active(&_serial)) {
641+
// note: tx_size correspond to size of HAL data transfer,
642+
// not the total amount of data in the buffer.
643+
// To compute size of data in buffer compare head and tail
644+
_serial.tx_size = size_intermediate;
645+
uart_attach_tx_callback(&_serial, _tx_complete_irq, size_intermediate);
646+
}
639647
}
640-
641-
/* There is no real error management so just return transfer size requested*/
648+
/* Return 0 if not ready else requested transfer size */
642649
return ret;
643650
}
644651

0 commit comments

Comments
 (0)