Skip to content

libtty: add support for TCXONC and TCSBRK#671

Draft
julianuziemblo wants to merge 9 commits into
masterfrom
julianuziemblo/RTOS-1355
Draft

libtty: add support for TCXONC and TCSBRK#671
julianuziemblo wants to merge 9 commits into
masterfrom
julianuziemblo/RTOS-1355

Conversation

@julianuziemblo

Copy link
Copy Markdown
Contributor

Description

Motivation and Context

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Chore (refactoring, style fixes, git/CI config, submodule management, no code logic changes)

How Has This Been Tested?

  • Already covered by automatic testing.
  • New test added: (add PR link here).
  • Tested by hand on: ia32-generic-qemu

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing linter checks and tests passed.
  • My changes generate no new compilation warnings for any of the targets.

Special treatment

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for the TCXONC (flow control) and TCSBRK (send break) ioctls in libtty, implementing the libtty_flow and libtty_sendbreak helper functions and updating output/ready checks to respect the new TF_OOFF and TF_SBRK flags. Feedback on these changes highlights several critical issues: first, using volatile sig_atomic_t for t_flags does not guarantee atomic read-modify-write operations, so standard C11 atomics should be used instead; second, a negative duration in libtty_sendbreak can cause an integer overflow and block usleep indefinitely, requiring input validation; third, libtty_flow lacks a default case to handle invalid actions; and finally, the return value of libtty_sendbreak should be propagated back through libtty_ioctl.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tty/libtty/libtty.h Outdated
/* cached optimizations */
char breakchars[4]; /* enough to hold \n, VEOF and VEOL. */
unsigned int t_flags;
volatile sig_atomic_t t_flags;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using volatile sig_atomic_t does not guarantee atomic read-modify-write operations (such as bitwise OR/AND). Since t_flags is accessed and modified across different execution contexts (e.g., user-space threads and potentially ISRs or driver threads) without a single shared lock, this can lead to race conditions where flag updates are lost. Please use standard C11 atomic types from <stdatomic.h> (e.g., atomic_uint or _Atomic unsigned int) to ensure thread-safe atomic operations.

	_Atomic unsigned int t_flags;
References
  1. When implementing atomic operations, prefer using standard library features (e.g., from <stdatomic.h>) over compiler-specific built-ins to ensure code portability and adherence to standards.
  2. To prevent hard-to-debug race conditions, always use atomic operations for read-modify-write access to variables shared between an Interrupt Service Routine (ISR) and another execution context, even if the chance of a race seems low.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is what I tried to achieve here - the reads being "atomic" (going in one go). The writes are always performed under a lock, because they depend on libtty_write or other functions that should see only one state on them under the lock. Because of this, atomics here seem like an overkill.

Comment thread tty/libtty/libtty.c Outdated
Comment thread tty/libtty/libtty.h Outdated
Comment thread tty/libtty/libtty.c
Comment thread tty/libtty/libtty.c
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

Unit Test Results

10 860 tests  ±0   10 189 ✅  - 1   53m 54s ⏱️ +45s
   670 suites ±0      670 💤 ±0 
     1 files   ±0        1 ❌ +1 

For more details on these failures, see this check.

Results for commit 1d8e4d1. ± Comparison against base commit 4f6a072.

This pull request removes 1 and adds 1 tests. Note that renamed tests count towards both.
phoenix-rtos-tests/mem/mprotect ‑ aarch64a53-zynqmp-qemu:phoenix-rtos-tests/mem/mprotect.test_mprotect.pages_in_parent_copied
phoenix-rtos-tests/mem/mprotect ‑ aarch64a53-zynqmp-qemu:phoenix-rtos-tests/mem/mprotect

♻️ This comment has been updated with latest results.

@julianuziemblo julianuziemblo force-pushed the julianuziemblo/RTOS-1355 branch 2 times, most recently from 0fd3083 to 725e4dc Compare June 17, 2026 08:58
@julianuziemblo

Copy link
Copy Markdown
Contributor Author

/gemini review

@julianuziemblo julianuziemblo force-pushed the julianuziemblo/RTOS-1355 branch from 725e4dc to 821f53c Compare June 17, 2026 09:10

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for TTY flow control (TCXONC) and sending break signals (TCSBRK), including a concrete implementation for the 16550 UART driver. The review feedback identifies a critical issue where a mutex is held during a sleep call in uart_sendbreak, which could block the interrupt thread and cause RX FIFO overruns. Additionally, the reviewer points out a type mismatch in the sendbreak callback signature, recommends using standard atomic operations from <stdatomic.h> instead of volatile sig_atomic_t for thread-safe flag modifications, and notes an unused flag definition.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tty/libtty/libtty.h Outdated
Comment thread tty/libtty/libtty.h Outdated
Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/libtty/libtty.h Outdated
Comment thread tty/libtty/libtty.h Outdated
Comment thread tty/libtty/libtty.c Outdated
Comment thread tty/libtty/libtty.c Outdated
Comment thread tty/libtty/libtty.c Outdated
Comment thread tty/libtty/libtty.c Outdated
Comment thread tty/libtty/libtty.c Outdated
@julianuziemblo

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements software flow control (IXON/IXOFF/IXANY), break signaling (TCSBRK), and input parity/break handling in the TTY library and the 16550 UART driver. While these additions significantly expand terminal capabilities, several critical issues were identified in the implementation. Specifically, the UART interrupt handler contains a potential buffer underflow and incorrectly associates parity/break errors with previously received characters. Additionally, the IXOFF flow control implementation introduces a deadlock risk and premature resume behavior, the libtty_flow function incorrectly returns positive values on success instead of zero, and the break transmitter busy-waits without yielding, which can cause high CPU utilization.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/libtty/libtty_disc.c
Comment thread tty/libtty/libtty.c
Comment thread tty/uart16550/uart16550.c Outdated
@julianuziemblo julianuziemblo force-pushed the julianuziemblo/RTOS-1355 branch from 919a9b1 to c7616a0 Compare June 18, 2026 12:35
@julianuziemblo

Copy link
Copy Markdown
Contributor Author

/gemini review

Comment thread tty/libtty/libtty.c Outdated
@datadog-phoenix-rtos

datadog-phoenix-rtos Bot commented Jun 18, 2026

Copy link
Copy Markdown

Pipelines

⚠️ Warnings

🚦 1 Pipeline job failed

ci | call-ci / test EMU (aarch64a53-zynqmp-qemu)   View in Datadog   GitHub Actions

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 1d8e4d1 | Docs | Give us feedback!

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces software flow control (IXON, IXOFF, IXANY) and additional terminal flags (PARMRK, INPCK, IGNPAR, NOFLSH) to the TTY library and UART 16550 driver, utilizing atomic operations for thread-safe flag updates. However, several critical issues need to be addressed. In libtty, removing mutex locks in libtty_close and calling condBroadcast outside the mutex in TCOON introduce race conditions, and libtty_write is incorrectly called with a size of 0 instead of 1 when sending CSTART. In the UART driver, sleep(100) is used instead of usleep, an off-by-one error in the buffer check can cause an overflow, normal characters are completely ignored in the interrupt handler, and parity error handling is incorrectly coupled with hardware configuration instead of being processed in software based on INPCK.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tty/libtty/libtty.c Outdated
Comment thread tty/libtty/libtty.c
Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/libtty/libtty.c
Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/uart16550/uart16550.c Outdated
@julianuziemblo julianuziemblo force-pushed the julianuziemblo/RTOS-1355 branch from c7616a0 to e361915 Compare June 18, 2026 14:49
Comment thread tty/libtty/libtty_disc.c Outdated
@julianuziemblo julianuziemblo force-pushed the julianuziemblo/RTOS-1355 branch from e361915 to 24c7dcc Compare June 18, 2026 14:59
@julianuziemblo

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds software flow control (IXON, IXOFF, IXANY), parity and break handling, and new ioctls (TCXONC, TCSBRK) to the TTY subsystem and UART 16550 driver, alongside migrating t_flags to atomic operations. The review feedback identifies critical issues, including a potential deadlock in libtty_putchar_helper from recursive locking, a race condition on REG_LCR in break_enable, and unconditional register reads in the UART interrupt handler that can discard valid characters. Additionally, the feedback points out a potential hang in libtty_flow due to blocking writes, a race condition on tx_fifo from unsynchronized access, and a duplicate header inclusion in libtty.h.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tty/libtty/libtty_disc.c Outdated
Comment thread tty/uart16550/uart16550.c
Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/libtty/libtty.c
Comment thread tty/libtty/libtty_disc.c
Comment on lines +225 to +227
const char cstop = CSTOP;
tx_write_ifspace(tty, &cstop, 1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Calling tx_write_ifspace here introduces a race condition on tty->tx_fifo. tx_write_ifspace modifies the transmit FIFO without acquiring tty->tx_mutex, which can run concurrently with a user-space thread calling libtty_write (which holds tty->tx_mutex). To ensure thread safety, use libtty_write with O_NONBLOCK instead, which properly synchronizes access to the transmit FIFO.

const char cstop = CSTOP;
			libtty_write(tty, &cstop, 1, O_NONBLOCK);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, but we already use it in libtty_disc.c all over the place without synchronization, so its a problem to solve in another PR.

Comment thread tty/libtty/libtty.h Outdated
@julianuziemblo julianuziemblo force-pushed the julianuziemblo/RTOS-1355 branch 2 times, most recently from c568f25 to f8818e0 Compare June 19, 2026 08:08
@julianuziemblo

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces software flow control (IXON, IXOFF, IXANY), parity/break handling, and atomic operations for t_flags in the TTY layer and UART 16550 driver, alongside new ioctls like TCXONC and TCSBRK. Key feedback highlights several critical issues: a buffer overflow in uart_interrupt under PARMRK due to an insufficient boundary check; swapped logic between TCSETS and TCSETSW ioctls; a lost wake-up race condition in libtty_close from removed mutex locks; unconditional clearing of TF_IOFF in libtty_read_helper even if CSTART fails to send; a potential deadlock/latency issue from holding uart->mutex while sleeping in break_enable; a missing callback signal when IXANY clears TF_OOFF; and an extremely low IXOFF threshold of 4 bytes that lacks hysteresis.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tty/uart16550/uart16550.c Outdated
Comment thread tty/libtty/libtty.c Outdated
Comment thread tty/libtty/libtty.c
Comment thread tty/libtty/libtty.c
Comment thread tty/uart16550/uart16550.c
Comment thread tty/libtty/libtty_disc.c Outdated
Comment thread tty/libtty/libtty_disc.h Outdated
@julianuziemblo julianuziemblo force-pushed the julianuziemblo/RTOS-1355 branch from f8818e0 to 1d8e4d1 Compare June 19, 2026 10:10
@julianuziemblo

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements software flow control (IXON, IXOFF, IXANY) and break condition handling (TCSBRK, TCXONC) in the TTY line discipline and UART 16550 driver, and transitions t_flags to atomic operations. Feedback highlights several critical issues: removing mutex locks in libtty_close introduces a lost wake-up race condition; reading REG_LSR in break_enable and calling fifo_freespace in libtty_read_helper without holding their respective mutexes cause data races; TCSETSF should drain the output queue before flushing the input queue to avoid missing incoming characters; and the break condition check in the UART interrupt handler must precede the parity error check to prevent incorrect handling.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tty/libtty/libtty.c
Comment thread tty/uart16550/uart16550.c
Comment thread tty/libtty/libtty.c
Comment on lines +163 to +172
/* IXOFF: send CSTART if we stopped the remote and the queue has space again */
if (CMP_FLAG(i, IXOFF)) {
if (fifo_freespace(tty->rx_fifo) >= LIBTTYDISC_INPUT_ON_THRESHOLD &&
(atomic_load_explicit(&tty->t_flags, memory_order_relaxed) & TF_IOFF) != 0) {
const char cstart = CSTART;
/* non-blocking write so that we don't deadlock */
libtty_write(tty, &cstart, 1, O_NONBLOCK);
atomic_fetch_and_explicit(&tty->t_flags, ~TF_IOFF, memory_order_relaxed);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling fifo_freespace(tty->rx_fifo) without holding tty->rx_mutex is a data race because the RX FIFO can be concurrently modified by the interrupt worker thread or other contexts. To avoid this race and prevent potential lock inversion (since libtty_write locks tx_mutex), we should lock and unlock rx_mutex strictly around the fifo_freespace check.

	/* IXOFF: send CSTART if we stopped the remote and the queue has space again */
	if (CMP_FLAG(i, IXOFF)) {
		mutexLock(tty->rx_mutex);
		int space = fifo_freespace(tty->rx_fifo);
		mutexUnlock(tty->rx_mutex);
		if (space >= LIBTTYDISC_INPUT_ON_THRESHOLD &&
				(atomic_load_explicit(&tty->t_flags, memory_order_relaxed) & TF_IOFF) != 0) {
			const char cstart = CSTART;
			/* non-blocking write so that we don't deadlock */
			libtty_write(tty, &cstart, 1, O_NONBLOCK);
			atomic_fetch_and_explicit(&tty->t_flags, ~TF_IOFF, memory_order_relaxed);
		}
	}

Comment thread tty/libtty/libtty.c
Comment on lines +542 to +548
case TCSETSF:
libtty_flush(tty, TCIFLUSH);
/* fallthrough */
case TCSETSW:
case TCSETSF: {
libtty_drain(tty);
/* fallthrough */
case TCSETS: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In TCSETSF, the input queue is flushed before draining the output queue. Since draining the output queue (libtty_drain) blocks and can take time, new input characters could arrive during this period and would not be flushed. To adhere to the standard behavior where all input received up to the point of the termios change is discarded, the output queue should be drained first, followed by flushing the input queue.

Suggested change
case TCSETSF:
libtty_flush(tty, TCIFLUSH);
/* fallthrough */
case TCSETSW:
case TCSETSF: {
libtty_drain(tty);
/* fallthrough */
case TCSETS: {
case TCSETSF:
libtty_drain(tty);
libtty_flush(tty, TCIFLUSH);
goto set_termios;
case TCSETSW:
libtty_drain(tty);
/* fallthrough */
case TCSETS:
set_termios: {

Comment thread tty/uart16550/uart16550.c Outdated
Comment on lines +199 to +230
if ((lsr & LSR_PE) != 0 && ((iflags & INPCK) != 0)) { /* parity error */
if ((iflags & IGNPAR) != 0) {
/* ignore characters with parity errors */
}
else {
if ((iflags & PARMRK) != 0) {
if (i + 2 < SW_BUF_SIZE) {
uart->buf[i++] = '\377';
uart->buf[i++] = '\0';
uart->buf[i++] = c;
}
/* TODO: else maybe signal SW overrun */
}
else {
if (i < SW_BUF_SIZE) {
uart->buf[i++] = '\0';
}
/* TODO: else maybe signal SW overrun */
}
}
}
else if ((lsr & LSR_BI) != 0) { /* break condition */
if ((iflags & IGNBRK) != 0) {
/* ignore break condition */
}
else {
if (i < SW_BUF_SIZE) {
uart->buf[i++] = '\0';
}
/* TODO: else maybe signal SW overrun */
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

A break condition (LSR_BI) typically also triggers a parity error (LSR_PE) because the line is held low. If LSR_PE is checked first and INPCK is enabled, the break condition will be incorrectly handled as a parity error. Reordering the checks to handle the break condition first ensures correct behavior.

			if ((lsr & LSR_BI) != 0) { /* break condition */
				if ((iflags & IGNBRK) != 0) {
					/* ignore break condition */
				}
				else {
					if (i < SW_BUF_SIZE) {
						uart->buf[i++] = '\\0';
					}
					/* TODO: else maybe signal SW overrun */
				}
			}
			else if ((lsr & LSR_PE) != 0 && ((iflags & INPCK) != 0)) { /* parity error */
				if ((iflags & IGNPAR) != 0) {
					/* ignore characters with parity errors */
				}
				else {
					if ((iflags & PARMRK) != 0) {
						if (i + 2 < SW_BUF_SIZE) {
							uart->buf[i++] = '\\377';
							uart->buf[i++] = '\\0';
							uart->buf[i++] = c;
						}
						/* TODO: else maybe signal SW overrun */
					}
					else {
						if (i < SW_BUF_SIZE) {
							uart->buf[i++] = '\\0';
						}
						/* TODO: else maybe signal SW overrun */
					}
				}
			}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant