Skip to content

arch/sim: Add PTY mode for simulated UART#19515

Open
LingaoM wants to merge 1 commit into
apache:masterfrom
LingaoM:sim-uart-pty
Open

arch/sim: Add PTY mode for simulated UART#19515
LingaoM wants to merge 1 commit into
apache:masterfrom
LingaoM:sim-uart-pty

Conversation

@LingaoM

@LingaoM LingaoM commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

The simulated UART driver currently opens the host path configured by
CONFIG_SIM_UARTx_NAME directly. This requires the host-side serial endpoint
to exist before NuttX opens the UART, so users and CI jobs need an external
setup step such as socat to create a PTY pair. It also means the host
endpoint path is effectively a build-time choice: changing the host device
path requires changing configuration and rebuilding, which is inconvenient
for tests that allocate a fresh PTY path on each run.

Add CONFIG_SIM_UART_PTY for Linux sim builds. When enabled, non-console
simulated UART ports allocate a host pseudoterminal from /dev/ptmx, put the
host master side in raw mode, and print the host slave path when the NuttX
UART is opened. The NuttX-side device name remains CONFIG_SIM_UARTx_NAME,
for example /dev/ttySIM0, so applications continue to use the normal NuttX
serial API.

Keep the option disabled by default so existing configurations still open
the configured host path directly. Console UART handling is also left on the
existing host-open path.

Also make host_uart_checkin() and host_uart_checkout() check the actual
POLLIN/POLLOUT bits returned by poll(). This avoids treating error-only or
unrelated poll events as readable or writable serial readiness.

The main benefit is simpler and more deterministic simulator integration: a
simulated UART can expose a real host-visible /dev/pts/N endpoint by itself,
without pre-creating a matching host device and without rebuilding NuttX
when the host PTY path changes. This is useful for host-side test scripts
and external protocol tools while keeping application code on the standard
NuttX UART interface.

Companion apps-side test branch:

https://github.com/LingaoM/nuttx-apps/tree/sim_uart_tester

Testing:

Host:

Ubuntu 22.04 x86_64

Board/config:

sim:nsh

Apps test code:

https://github.com/LingaoM/nuttx-apps/tree/sim_uart_tester

Common test configuration:

CONFIG_NSH_BUILTIN_APPS=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_SIM_UART_NUMBER=1
CONFIG_SIM_UART0_NAME="/dev/ttySIM0"
CONFIG_SIM_UART_PTY=y

DMA-mode build and test:

1. Configure sim:nsh with the companion apps tree:

     ./tools/configure.sh -a ../nuttx-apps sim:nsh

2. Enable the common test configuration above and keep DMA enabled:

     CONFIG_SIM_UART_DMA=y
     CONFIG_SERIAL_TXDMA=y
     CONFIG_SERIAL_RXDMA=y

3. Build:

     make clean
     make -j16

4. Start NuttX:

     ./nuttx

5. In NSH, run the hello test app:

     nsh> hello
     /dev/ttySIM0 connected to pseudotty: /dev/pts/73

6. In another terminal, run the host-side tester from the companion apps
   branch with the printed PTY path:

     cd ../nuttx-apps
     ./examples/hello/test_sim_uart_pty.py /dev/pts/73

7. The host-side tester sends 32768 bytes from the host to NuttX and
   receives 49152 bytes from NuttX to the host. The payload includes
   non-text binary bytes. Both sides validate deterministic payload
   contents and checksums, then exchange an ACK.

8. Observed host-side output:

     HOST_OPEN: /dev/pts/73
     HOST_TX: 32768 bytes checksum=0x1f9989f4
     HOST_RX: 49152 bytes checksum=0x06a45c69
     HOST_TX: ACK
     TEST PASSED

9. Observed NuttX output:

     sim_uart_pty_test: binary RX 32768 TX 49152 passed

Non-DMA build and test:

1. Disable DMA for the same sim:nsh configuration:

     # CONFIG_SIM_UART_DMA is not set
     # CONFIG_SERIAL_TXDMA is not set
     # CONFIG_SERIAL_RXDMA is not set

2. Refresh the configuration and rebuild. On this host, the installed
   Python olddefconfig shim is broken, so I refreshed Kconfig directly
   with kconfig-conf and the same environment that the NuttX Makefile
   passes to Kconfig:

     APPSDIR=/mnt/ssd/code/code/nuttx-apps \
     APPSBINDIR=/mnt/ssd/code/code/nuttx-apps \
     BINDIR=/mnt/ssd/code/code/nuttx \
     EXTERNALDIR=/mnt/ssd/code/code/nuttx/dummy \
       kconfig-conf --olddefconfig Kconfig

     make clean
     make -j16

3. Repeated the same runtime steps as the DMA test:

     ./nuttx
     nsh> hello
     cd ../nuttx-apps
     ./examples/hello/test_sim_uart_pty.py /dev/pts/73

4. Observed the same successful binary transfer result:

     HOST_TX: 32768 bytes checksum=0x1f9989f4
     HOST_RX: 49152 bytes checksum=0x06a45c69
     HOST_TX: ACK
     TEST PASSED
     sim_uart_pty_test: binary RX 32768 TX 49152 passed

After the non-DMA test, I restored the default DMA configuration, rebuilt,
and reran the same binary PTY test successfully so the final local build
state was back on CONFIG_SIM_UART_DMA=y.

image

@github-actions github-actions Bot added Area: Documentation Improvements or additions to documentation Arch: simulator Issues related to the SIMulator Size: M The size of the change in this PR is medium labels Jul 24, 2026
@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

Comment thread arch/sim/src/sim/posix/sim_hostuart.c Outdated
Comment thread arch/sim/src/sim/posix/sim_hostuart.c Outdated
The simulated UART driver currently opens the host path configured by
CONFIG_SIM_UARTx_NAME directly. This requires the host-side serial endpoint
to exist before NuttX opens the UART, so users and CI jobs need an external
setup step such as socat to create a PTY pair. It also means the host
endpoint path is effectively a build-time choice: changing the host device
path requires changing configuration and rebuilding, which is inconvenient
for tests that allocate a fresh PTY path on each run.

Add CONFIG_SIM_UART_PTY for Linux sim builds. When enabled, non-console
simulated UART ports allocate a host pseudoterminal from /dev/ptmx, put the
host master side in raw mode, and print the host slave path when the NuttX
UART is opened. The NuttX-side device name remains CONFIG_SIM_UARTx_NAME,
for example /dev/ttySIM0, so applications continue to use the normal NuttX
serial API.

Keep the option disabled by default so existing configurations still open
the configured host path directly. Console UART handling is also left on the
existing host-open path.

Also make host_uart_checkin() and host_uart_checkout() check the actual
POLLIN/POLLOUT bits returned by poll(). This avoids treating error-only or
unrelated poll events as readable or writable serial readiness.

The main benefit is simpler and more deterministic simulator integration: a
simulated UART can expose a real host-visible /dev/pts/N endpoint by itself,
without pre-creating a matching host device and without rebuilding NuttX
when the host PTY path changes. This is useful for host-side test scripts
and external protocol tools while keeping application code on the standard
NuttX UART interface.

Companion apps-side test branch:

  https://github.com/LingaoM/nuttx-apps/tree/sim_uart_tester

Testing:

  Host:

    Ubuntu 22.04 x86_64

  Board/config:

    sim:nsh

  Apps test code:

    https://github.com/LingaoM/nuttx-apps/tree/sim_uart_tester

  Common test configuration:

    CONFIG_NSH_BUILTIN_APPS=y
    CONFIG_EXAMPLES_HELLO=y
    CONFIG_SIM_UART_NUMBER=1
    CONFIG_SIM_UART0_NAME="/dev/ttySIM0"
    CONFIG_SIM_UART_PTY=y

  DMA-mode build and test:

    1. Configure sim:nsh with the companion apps tree:

         ./tools/configure.sh -a ../nuttx-apps sim:nsh

    2. Enable the common test configuration above and keep DMA enabled:

         CONFIG_SIM_UART_DMA=y
         CONFIG_SERIAL_TXDMA=y
         CONFIG_SERIAL_RXDMA=y

    3. Build:

         make clean
         make -j16

    4. Start NuttX:

         ./nuttx

    5. In NSH, run the hello test app:

         nsh> hello
         /dev/ttySIM0 connected to pseudotty: /dev/pts/73

    6. In another terminal, run the host-side tester from the companion apps
       branch with the printed PTY path:

         cd ../nuttx-apps
         ./examples/hello/test_sim_uart_pty.py /dev/pts/73

    7. The host-side tester sends 32768 bytes from the host to NuttX and
       receives 49152 bytes from NuttX to the host. The payload includes
       non-text binary bytes. Both sides validate deterministic payload
       contents and checksums, then exchange an ACK.

    8. Observed host-side output:

         HOST_OPEN: /dev/pts/73
         HOST_TX: 32768 bytes checksum=0x1f9989f4
         HOST_RX: 49152 bytes checksum=0x06a45c69
         HOST_TX: ACK
         TEST PASSED

    9. Observed NuttX output:

         sim_uart_pty_test: binary RX 32768 TX 49152 passed

  Non-DMA build and test:

    1. Disable DMA for the same sim:nsh configuration:

         # CONFIG_SIM_UART_DMA is not set
         # CONFIG_SERIAL_TXDMA is not set
         # CONFIG_SERIAL_RXDMA is not set

    2. Refresh the configuration and rebuild. On this host, the installed
       Python olddefconfig shim is broken, so I refreshed Kconfig directly
       with kconfig-conf and the same environment that the NuttX Makefile
       passes to Kconfig:

         APPSDIR=/mnt/ssd/code/code/nuttx-apps \
         APPSBINDIR=/mnt/ssd/code/code/nuttx-apps \
         BINDIR=/mnt/ssd/code/code/nuttx \
         EXTERNALDIR=/mnt/ssd/code/code/nuttx/dummy \
           kconfig-conf --olddefconfig Kconfig

         make clean
         make -j16

    3. Repeated the same runtime steps as the DMA test:

         ./nuttx
         nsh> hello
         cd ../nuttx-apps
         ./examples/hello/test_sim_uart_pty.py /dev/pts/73

    4. Observed the same successful binary transfer result:

         HOST_TX: 32768 bytes checksum=0x1f9989f4
         HOST_RX: 49152 bytes checksum=0x06a45c69
         HOST_TX: ACK
         TEST PASSED
         sim_uart_pty_test: binary RX 32768 TX 49152 passed

  After the non-DMA test, I restored the default DMA configuration, rebuilt,
  and reran the same binary PTY test successfully so the final local build
  state was back on CONFIG_SIM_UART_DMA=y.

Assisted-by: Claude:Claude-Fable-5
Signed-off-by: Lingao Meng <menglingao@xiaomi.com>

If ``CONFIG_SIM_UART_PTY`` is enabled, NuttX creates the host
pseudoterminal when the simulated UART is opened and prints the host
PTY slave path.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please explain the issues of the previous/existing solution and how it, like you did in the Summary

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

Labels

Arch: simulator Issues related to the SIMulator Area: Documentation Improvements or additions to documentation Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants