FDomain can run over a physical serial port (UART), allowing developers to
interact with and debug Fuchsia devices that have no active network or USB
connectivity.

## Overview

The UART transport runs the [FDomain](https://fuchsia.dev/docs/development/tools/ffx/fdomain) protocol over an asynchronous
serial line (such as `/dev/ttyUSB0` on Linux, or a host UNIX domain socket for a
QEMU emulator) connected to a Fuchsia target device's serial port (exposed under
`/dev/class/serial/`).

Communication operates at a high line rate (defaulting to **1,000,000 baud** )
using the **ResendSP** sliding-window framing protocol, which provides
reliable bidirectional delivery with checksums, acknowledgments, and session
tracking across device reboots.

## Prerequisites

### Host Serial Port Permissions

On Linux hosts, accessing TTY character devices (e.g., `/dev/ttyUSB0`) requires
membership in the `dialout` group:

    sudo usermod -a -G dialout $USER

You must log out and back in (or run `newgrp dialout`) for the group membership
to take effect.

### Enable UART Target Discovery in `ffx`

By default, `ffx` target discovery monitors mDNS and USB discovery streams. To
enable the `UartWatcher` subsystem to monitor active UART connections and list
them in `ffx target list`, set `connectivity.enable_uart` to `true`:

    ffx config set connectivity.enable_uart true

### Target Hardware Configuration

On physical hardware platforms (such as boards using DesignWare 8250/APB UART
controllers), high-speed serial rates (1,000,000 baud) are calibrated using
Divisor Latch Fraction (DLF) registers to minimize bit timing error.

Because single-port targets share the physical serial line between the kernel
console and userspace services, enabling `ffx` over UART requires handing off
serial port ownership to the userspace FDomain UART driver (`fdomain-uart-driver`)
via kernel command-line arguments (for example, in `local/BUILD.gn` developer
overrides):

- `dev.fdomain.uart=true`: Enables the target-side `fdomain-uart-driver` component to discover and bind to the serial port in `/dev/class/serial/`.
- `dev.fdomain.uart.baud=1000000`: Configures the target serial port baud rate.
- `kernel.serial=none`: Disables the Zircon kernel serial console so kernel debug prints do not collide with binary `ResendSP` frames.
- `console.shell=false`: Disables the userspace interactive serial console shell (`console.cm`).

> [!NOTE]
> **Note:** Disabling `kernel.serial` suppresses raw early-boot serial output and synchronous kernel panic dumps over the physical UART pins. Once userspace boots and `ffx` connects, kernel debug logs (`klog` / `dlog`) and component logs remain available through `ffx log`.

## Connection Management

Unlike network or USB interfaces which support automatic background enumeration,
serial ports are shared physical resources with no standard bus discovery
mechanism. The `ffx uart` tool provides explicit subcommands to manage the
connection lifecycle and ensure mutual exclusion across host tools.

### Connecting to a Target

To establish a UART connection to a physical device:

    ffx -t /dev/ttyUSB0 uart connect

*(You may also include the `uart:` prefix, e.g.,
`ffx -t uart:/dev/ttyUSB0 uart connect`, or specify a running QEMU emulator
instance name such as `ffx -t fuchsia-emulator uart connect`; see
[Using Fuchsia Emulators](https://fuchsia.dev/fuchsia-src/development/tools/ffx/uart#using-emulators)).*

`ffx uart connect` defaults to **1,000,000 baud**. You can specify a custom baud
rate if your board configuration requires it:

    ffx -t /dev/ttyUSB0 uart connect --baud 115200

When connecting:
1. The tool acquires an exclusive file lock (`flock`) on the serial device to
prevent concurrent access from other processes.
2. It configures the TTY in raw mode with 8 data bits, 1 stop bit, no parity
(8N1), and the specified baud rate.
3. It spawns the `ffx-uart-driver` background daemon to multiplex FDomain
traffic.
4. Connection metadata is recorded under the `ffx` shared data directory
(`ffx_uart/`).

### Running Commands over UART

Once connected, you can run any standard `ffx` command targeting the UART
device using the `uart:` address prefix:

    ffx -t uart:/dev/ttyUSB0 target echo

    ffx -t uart:/dev/ttyUSB0 log

### Probing Connection Health

To verify whether a physical serial port is responsive and communicating with
the target's FDomain UART service:

    ffx -t /dev/ttyUSB0 uart probe

The `probe` command performs the following checks:
\* If a background driver is already active, it verifies driver process health
and reports the current status.
\* If no driver is active, it opens the port temporarily, performs a protocol
negotiation handshake with the target, measures the round-trip time (RTT), and
reports whether the service is responsive.

### Listing Active Connections

To view all active UART driver instances and their associated devices:

    ffx uart list

Example output:

    TARGET                         PID      BAUD     PROTOCOL   STATUS
    /dev/ttyUSB0                   12345    1000000  ResendSP Connected

### Target Discovery

When `connectivity.enable_uart` is enabled and the UART background driver is
active, `ffx` automatically discovers the target:

    ffx target list

Example output:

    NAME                      SERIAL          TYPE           STATE      ADDRS/IP              MANUAL    RCS
    fuchsia-54b7-e1d8-7af6    1234567890ab    minimal.x64    Product    [uart:/dev/ttyUSB0]   N         Y

`ffx` discovers UART targets passively by monitoring connection metadata
written by `ffx-uart-driver`. The target's nodename and serial number are
obtained by the driver upon connection and stored in metadata, allowing
discovery to detect and enumerate connected devices immediately without
contending for the physical serial port. If the same device is also connected
over USB or network, `ffx` merges the addresses under a single target entry by
matching the hardware serial number.

### Monitoring Status and Metrics

To inspect real-time transmission statistics for an active connection:

    ffx -t /dev/ttyUSB0 uart status

This queries the driver daemon's local control socket and metadata to report
operational details including:
\* Target path, 16-character target ID, daemon PID, and driver log file path.
\* Discovered target nodename and hardware serial number.
\* Connection status, configured baud rate, and active protocol (`ResendSP`).
\* Estimated round-trip latency (`Estimated RTT`), retransmission count, CRC
checksum errors, connection drops, and handshake failures.
\* Last read/write activity timestamps, outgoing queue length, stall detection
warnings, and the most recent channel error reported by the target (`Last Target Error`).

### Diagnosing and Repairing Connection Issues

To diagnose host-side UART connection problems across all connections or for a
specific target:

    ffx uart doctor

    ffx -t /dev/ttyUSB0 uart doctor

`ffx uart doctor` executes a sequence of diagnostic checks:
\* **Connection Exists** : Verifies that an active connection or metadata record
exists for the target.
\* **Metadata and Socket Files** : Inspects `<shared_data>/ffx_uart/` for empty or
corrupted JSON metadata files, orphaned `.sock` / `.sock.control` UNIX domain
sockets, and metadata referencing dead daemon PIDs.
\* **Daemon Process Liveness** : Verifies that the `ffx-uart-driver` process is
running (not a zombie) and responsive on its control socket.
\* **Serial Port Lock** : Probes the TTY character device with a non-blocking
exclusive file lock (`flock`) to detect contention from another process or a
leaked `ffx-uart-driver` instance.
\* **Session Stall Detection**: Inspects live control socket metrics to detect
read-stalled sessions (where outgoing writes have received no target response
beyond the 5-second stall threshold) or repeated handshake failures.

To automatically repair detected issues where possible, pass `--fix`:

    ffx uart doctor --fix

When `--fix` is enabled, the doctor tool removes stale or corrupted metadata and
socket files (while sparing targets that have an active connection), terminates
unresponsive or leaked `ffx-uart-driver` daemons, and restarts stalled
connections using cached target parameters.

### Disconnecting

To terminate the background driver and release the serial device lock:

    ffx -t /dev/ttyUSB0 uart disconnect

Releasing the connection frees the serial device for other uses, such as
interactive debug consoles or firmware flashing.

### Reconnecting

To restart the background driver using saved metadata (for instance, after a
cable disconnect or transient host issue):

    ffx -t /dev/ttyUSB0 uart connect --reconnect

## Using Fuchsia Emulators

You can run `ffx` over UART locally using the QEMU emulator (`--engine qemu`)
without physical hardware. Just like physical targets, emulators do not expose
a `uart:` address in `ffx target list` until you explicitly run
`ffx uart connect`.

### Starting the Emulator with UART Enabled

Start the QEMU emulator with `-c "dev.fdomain.uart=true"` and `--console` (or
`--headless`):

    ffx emu start -c "dev.fdomain.uart=true" --console --engine qemu

- `-c "dev.fdomain.uart=true"`: Passes the kernel command-line argument that starts `fdomain-uart-driver` on the target and configures QEMU's second emulated 16550 serial port (`uart16550`) as a host UNIX domain socket.
- `--console` (or `--headless`): Attaches serial port 0 to the interactive console while serial port 1 carries FDomain UART traffic. Use `--headless` for non-interactive runs.
- `--net none` (optional): Disables emulated networking to test a pure UART-only environment.

### Connecting and Running Commands on the Emulator

Connect the host UART driver using the emulator's instance name:

    ffx -t fuchsia-emulator uart connect

`ffx uart connect` resolves `fuchsia-emulator` to the running emulator
instance's serial socket on the host and spawns `ffx-uart-driver`. Once
connected, `ffx target list` includes `uart:fuchsia-emulator` in the target's
address list.

When both network and UART addresses are available on an emulator, `ffx`
defaults to the network transport. Specify the `uart:` prefix (or set it as your
default target with `fx set-device uart:fuchsia-emulator`) to route commands
over the emulated UART link:

    ffx -t uart:fuchsia-emulator target echo

When finished, disconnect the UART daemon and stop the emulator:

    ffx -t fuchsia-emulator uart disconnect
    ffx emu stop

## Architecture and Framing

    +---+
    |                        Host Machine                         |
    |                                                             |
    |   +---+        +---+   |
    |   |  ffx CLI   | <---> | ffx-uart-driver (Daemon)      |   |
    |   +---+ (UNIX  +---+   |
    |                  Socket)                |                   |
    |                                         | (ResendSP Frames) |
    |                                         v                   |
    |                              [/dev/ttyUSB0] (flock)         |
    +---+
                                              |
                                              | Physical Serial Cable
                                              | (1,000,000 Baud, 8N1)
                                              v
    +---+
    |                       Target Hardware                       |
    |                                                             |
    |                [/dev/class/serial/<instance>]               |
    |                                 |                           |
    |                                 v                           |
    |               +---+         |
    |               | fdomain-uart-driver -> RCS        |         |
    |               +---+         |
    +---+

### ResendSP Framing Protocol

The UART transport uses the **ResendSP** framing protocol to stream data over
raw byte-oriented serial links:
\* **Sliding Window Go-Back-N** : Supports pipelined transmission of up to 64
in-flight frames to maximize bandwidth utilization across both local serial
cables and high-latency bridged links.
\* **Flow Control \& Backpressure** : Cumulative acknowledgments and per-channel
flow control prevent buffer overflows on embedded targets.
\* **Integrity Checksums** : An 8-bit header checksum (`HdrChk`, CRC-8 polynomial
`0x07`) on the 13-byte frame header and a 32-bit IEEE 802.3 CRC (`CRC-32`) on
frame payloads detect transmission errors caused by electrical line noise.
\* **Reboot Detection**: Session tokens negotiated during the Channel 0 handshake
allow the host and target to detect resets and resynchronize stream state
automatically without rebooting the peer.

## Troubleshooting

- **Stale Metadata, Unresponsive Daemon, or Stalled Session** : Run `ffx uart doctor` to diagnose host-side state, port lock contention, and control socket health, or `ffx uart doctor --fix` to automatically clean up and repair issues.
- **Permission Denied opening `/dev/ttyUSB0`** : Ensure your user account is in the `dialout` group (`id $USER`), and verify device permissions.
- **Device Busy (`EWOULDBLOCK` / `EAGAIN` during `connect`)** : Another process holds an exclusive lock on the TTY device. Verify that no other serial terminal programs (e.g., `minicom`, `screen`, `picocom`) are open on the port.
- **Handshake Timeout during `connect` or `probe`** : Check that the physical TX/RX lines are wired correctly, the bootloader UART mux and clock are configured for 1,000,000 baud, and the target booted with `dev.fdomain.uart=true`, `kernel.serial=none`, and `console.shell=false`.

[](https://issues.fuchsia.dev/issues/new?component=1439230&template=1881640&description=Issue+on:+https://fuchsia.dev/docs/development/tools/ffx/uart.md "Report a bug or suggest improvements on this page") [](https://cs.opensource.google/fuchsia/fuchsia/+/main:docs/development/tools/ffx/uart.md "View the source code of this page") [](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/development/tools/ffx/uart.md&cloneRepo=fuchsia/fuchsia:refs/heads/main&fullPath=fuchsia/docs/development/tools/ffx/uart.md "Edit the source code of this page")