# `brawl:device/uart`

## Overview

The **`brawl:device/uart`** interface defines a portable, minimal **Universal Serial Receiver/Transmitter (UART)** API for WebAssembly (WASM) modules. It provides a consistent way to control and monitor UARTs across diverse embedded targets - ranging from microcontrollers to emulators - while remaining fully compatible with the WASM MVP specification.

Each function follows a simple convention: it returns a signed 32-bit integer, where negative values represent standardized error codes and non-negative values represent valid results or success indicators.  This design keeps the ABI deterministic, low-overhead, and portable between environments, enabling WASM applications to interact directly with physical or virtual UART devices without callbacks, threads, or host-specific extensions.

## Implementation Notes

This section outlines the expected behavior and conventions used by the `brawl:device/uart` interface. It provides guidance for both host implementers and guest developers to ensure consistent behavior across WASM targets.

#### UART Bus Enumeration

`uartCount()` returns the total number of UARTs available on the target. `uartList()` populates a memory buffer with a list of available bus identifiers (`uint8_t`) in the host environment’s canonical order.  Each bus identifier can be passed directly to `uartOpen()` to obtain a handle for further operations.

#### Open and Configure

Before utilizing a UART, always call `uartCapabilities()` to verify that it supports the desired modes. Use `uartOpen()` to reserve the UART and obtain a handle; this handle is passed to all subsequent operations. When the UART is no longer needed, release it using `uartClose()`. UARTs remain inactive until configured with calls to `uartSetBaud()`, `uartSetFormat()`, `uartSetFlow()`, `uartSetTimeouts()`, `uartSetBuffers()`, `uartSetLoopback()` and optionally `uartSetRS485()` - to define the UART behavior and electrical characteristics.

#### Software UART (software serial, bit-banged on GPIO)

A software UART, TX/RX can be synthesized on GPIO pins by toggling and sampling at fixed baud timing. The host reserves the pins and implements 8N1 framing in software. Maximum reliable baud depends on timing jitter of the host. Once opened, the software UART handle behaves the same as a hardware handle for I/O and configuration calls.

To detect if the host supports a software implementation, call `uartModuleCapabilities()` check for the `UART_CAPS_SW_PORT` capability, a UART can then be created using the `uartOpenOnPins()` function.

#### Error Codes

All `brawl:device/uart` functions return a signed 32-bit integer (`int32_t`), which encodes both result and error information:

| Value      |                                                                       |
|------------|-----------------------------------------------------------------------|
| `ret < 0`  | an error occurred.                                                    |
| `ret == 0` | operation completed successfully (or no value to return).             |
| `ret > 0`  | operation succeeded and returned a value (e.g., UART data or handle). |

All memory access operations are bounds-checked by the host; going outside will return a `UART_ERROR_MEMORY_OOB` error.

## Constants

#### UART Errors

All functions return a signed 32-bit integer (`int32_t`).  If the value is **negative**, it represents an error code from the table below:

| Constant                    |                                                                      |
|-----------------------------|----------------------------------------------------------------------|
| `UART_ERROR_BUSY`           | The UART is already in use or reserved by another handle.            |
| `UART_ERROR_UNSUPPORTED`    | The requested operation or capability is not supported on this UART. |
| `UART_ERROR_TIMEOUT`        | The operation did not complete within the expected time.             |
| `UART_ERROR_INVALID_HANDLE` | The handle provided was invalid for the operation requested.         |
| `UART_ERROR_INVALID_ARG`    | The argument provided was invalid for the operation requested.       |
| `UART_ERROR_MEMORY_OOB`     | The requested operation would result in a MEMORY_OOB Error.          |

A return value **`>= 0`** indicates success - either `0` for non-returning operations or a positive value for operations that produce a result.

#### UART Capabilities

Each UART can support one or more capabilities, reported by `uartModuleCapabilities()` and `uartCapabilities()`. These are bit-flags and may be combined using bitwise OR.

| Constant (Flag)          |                                                   |
|--------------------------|---------------------------------------------------|
| `UART_CAPS_BAUD_CUSTOM`  | Supports arbitrary baud rates (not just presets). |
| `UART_CAPS_DATABITS_5`   | Supports 5 data bits.                             |
| `UART_CAPS_DATABITS_6`   | Supports 6 data bits.                             |
| `UART_CAPS_DATABITS_7`   | Supports 7 data bits.                             |
| `UART_CAPS_DATABITS_8`   | Supports 8 data bits.                             |
| `UART_CAPS_DATABITS_9`   | Supports 9 data bits.                             |
| `UART_CAPS_PARITY_NONE`  | Supports no parity.                               |
| `UART_CAPS_PARITY_EVEN`  | Supports even parity.                             |
| `UART_CAPS_PARITY_ODD`   | Supports odd parity.                              |
| `UART_CAPS_STOPBITS_1`   | Supports 1 stop bit.                              |
| `UART_CAPS_STOPBITS_2`   | Supports 2 stop bits.                             |
| `UART_CAPS_FLOW_RTSCTS`  | Supports hardware flow control.                   |
| `UART_CAPS_FLOW_XONXOFF` | Supports software flow control.                   |
| `UART_CAPS_MODEM_LINES`  | Supports DTR/DSR/DCD/RI signals.                  |
| `UART_CAPS_RS485`        | Supports RS-485 protocol                          |
| `UART_CAPS_DMA`          | Supports DMA / large buffer transfers.            |
| `UART_CAPS_LOOPBACK`     | Supports internal loopback.                       |
| `UART_CAPS_SW_PORT`      | Supports creating a UART on arbitrary GPIO pins.  |

Combine capability flags with the bitwise OR operator (`|`) to describe a UART that supports multiple features.

#### UART Parity

Represents the parity of the UART.

| Constant           |              |
|--------------------|--------------|
| `UART_PARITY_NONE` | No parity.   |
| `UART_PARITY_EVEN` | Even parity. |
| `UART_PARITY_ODD`  | Odd parity.  |

#### UART Stop-Bits

Configures the stop bits of the UART.

| Constant          |              |
|-------------------|--------------|
| `UART_STOPBITS_1` | 1 stop bit.  |
| `UART_STOPBITS_2` | 2 stop bits. |

#### UART Flow Control

Configures the flow control of the UART.

| Constant            |                        |
|---------------------|------------------------|
| `UART_FLOW_OFF`     | No flow control.       |
| `UART_FLOW_RTSCTS`  | Hardware flow control. |
| `UART_FLOW_XONXOFF` | Software flow control. |

#### UART Flush

Flush the UART.

| Constant          |                           |
|-------------------|---------------------------|
| `UART_FLUSH_RX`   | Flush only RX buffer.     |
| `UART_FLUSH_TX`   | Flush only TX buffer.     |
| `UART_FLUSH_BOTH` | Flush Both RX/TX buffers. |

#### UART Line Status

Specifies the line status of the UART, these are bit-flags and may be combined using bitwise OR.

| Constant (Flag)       |                                 |
|-----------------------|---------------------------------|
| `UART_STATUS_OVERRUN` | Receiver overrun (data lost).   |
| `UART_STATUS_PARITY`  | Parity error.                   |
| `UART_STATUS_FRAMING` | Framing error (bad stop bit).   |
| `UART_STATUS_BREAK`   | Break condition being detected. |

#### UART Modem Signals

Specifies the modem signals of the UART, these are bit-flags and may be combined using bitwise OR.

| Constant (Flag)        |                        |
|------------------------|------------------------|
| `UART_SIGNAL_CTS`      | Clear to send.         |
| `UART_SIGNAL_DSR`      | Data set ready.        |
| `UART_SIGNAL_DCD`      | Data carrier detect.   |
| `UART_SIGNAL_RI`       | Ring indicator.        |
| `UART_SIGNAL_RTS`      | Ready to send.         |
| `UART_SIGNAL_DTR`      | Data terminal ready.   |

## Application Programmer Interface (API)

```
//--------------------------------------------------------------------------
// brawl:device/uart

// UART errors
enum UART_ERROR {
                        // >= 0              no error
  UART_ERROR_BUSY           = -1,         // port already open / TX in progress (where exclusive)
  UART_ERROR_UNSUPPORTED    = -2,         // feature not supported on this UART
  UART_ERROR_TIMEOUT        = -3,         // RX/TX timeout
  UART_ERROR_INVALID_HANDLE = -4,         // invalid handle
  UART_ERROR_INVALID_ARG    = -5,         // invalid argument
  UART_ERROR_MEMORY_OOB     = -6          // memory OOB
};

// UART capabilities (flags)
enum UART_CAPS {
  UART_CAPS_BAUD_CUSTOM     = (1u << 0),  // arbitrary baud rates (not just presets)
  UART_CAPS_DATABITS_5      = (1u << 1),
  UART_CAPS_DATABITS_6      = (1u << 2),
  UART_CAPS_DATABITS_7      = (1u << 3),
  UART_CAPS_DATABITS_8      = (1u << 4),
  UART_CAPS_DATABITS_9      = (1u << 5),
  UART_CAPS_PARITY_NONE     = (1u << 6),
  UART_CAPS_PARITY_EVEN     = (1u << 7),
  UART_CAPS_PARITY_ODD      = (1u << 8),
  UART_CAPS_STOPBITS_1      = (1u << 9),
  UART_CAPS_STOPBITS_2      = (1u << 10),
  UART_CAPS_FLOW_RTSCTS     = (1u << 11), // hardware flow control
  UART_CAPS_FLOW_XONXOFF    = (1u << 12), // software flow control
  UART_CAPS_MODEM_LINES     = (1u << 13), // DTR/DSR/DCD/RI signals
  UART_CAPS_RS485           = (1u << 14), // RS-485 driver enable support
  UART_CAPS_DMA             = (1u << 15), // DMA / large buffer transfers
  UART_CAPS_LOOPBACK        = (1u << 16), // internal loopback
  UART_CAPS_SW_PORT         = (1u << 17)  // create a UART on arbitrary GPIO pins
};

// UART parity
enum UART_PARITY {
  UART_PARITY_NONE          = 0,
  UART_PARITY_EVEN          = 1,
  UART_PARITY_ODD           = 2
};

// UART stopbits
enum UART_STOPBITS {
  UART_STOPBITS_1           = 0,
  UART_STOPBITS_2           = 1
};

// UART flow control
enum UART_FLOW {
  UART_FLOW_OFF             = 0,
  UART_FLOW_RTSCTS          = 1,
  UART_FLOW_XONXOFF         = 2
};

// UART flush
enum UART_FLUSH {
  UART_FLUSH_RX             = 1,
  UART_FLUSH_TX             = 2,
  UART_FLUSH_BOTH           = 3
};

// UART status
enum UART_STATUS {
  UART_STATUS_OVERRUN       = (1u << 0),
  UART_STATUS_PARITY        = (1u << 1),
  UART_STATUS_FRAMING       = (1u << 2),
  UART_STATUS_BREAK         = (1u << 3)
};

// UART signal
enum UART_SIGNAL {
  // get
  UART_SIGNAL_CTS           = (1u << 0),
  UART_SIGNAL_DSR           = (1u << 1),
  UART_SIGNAL_DCD           = (1u << 2),
  UART_SIGNAL_RI            = (1u << 3),

  // set/clear
  UART_SIGNAL_RTS           = (1u << 8),
  UART_SIGNAL_DTR           = (1u << 9)
};

int32_t // UART_ERROR|UART_CAPS
uartModuleCapabilities(void);
```

#### Discovery

```
int32_t // UART_ERROR|int32_t
uartCount(void);

int32_t // UART_ERROR|int32_t
uartList(int32_t mem_ptr, int32_t max_items);

int32_t // UART_ERROR|UART_CAPS
uartCapabilities(int32_t uartBus);
```

#### Open and Configure

```
int32_t // UART_ERROR|int32_t
uartOpen(int32_t uartBus);

int32_t // UART_ERROR|int32_t
uartOpenOnPins(int32_t tx_pin, int32_t rx_pin);

int32_t // UART_ERROR|void
uartSetBaud(int32_t handle, int32_t baud);

int32_t // UART_ERROR|void
uartSetFormat(int32_t handle, int32_t databits, enum UART_PARITY parity, enum UART_STOPBITS stopbits);

int32_t // UART_ERROR|void
uartSetFlow(int32_t handle, enum UART_FLOW flow);

int32_t // UART_ERROR|void
uartSetTimeouts(int32_t handle, int32_t rx_timeout_us, int32_t tx_timeout_us);

int32_t // UART_ERROR|void
uartSetBuffers(int32_t handle, int32_t rx_bytes, int32_t tx_bytes);

int32_t // UART_ERROR|void
uartSetLoopback(int32_t handle, int32_t enable);

int32_t // UART_ERROR|void
uartSetRS485(int32_t handle, int32_t enable, int32_t rts_active_high);

int32_t // UART_ERROR|void
uartSetRS485Timing(int32_t handle, int32_t pre_us, int32_t post_us);

int32_t // UART_ERROR|void
uartClose(int32_t handle);
```

#### I/O (Linear Memory Buffers)

```
int32_t // UART_ERROR|int32_t
uartWrite(int32_t handle, int32_t tx_ptr, int32_t len, int32_t timeout_us);

int32_t // UART_ERROR|int32_t
uartRead(int32_t handle, int32_t rx_ptr, int32_t len, int32_t timeout_us);

int32_t // UART_ERROR|int32_t
uartAvailable(int32_t handle);

int32_t // UART_ERROR|int32_t
uartTxSpace(int32_t handle);

int32_t // UART_ERROR|int32_t
uartFlush(int32_t handle, enum UART_FLUSH which);

int32_t // UART_ERROR|void
uartSendBreak(int32_t handle, int32_t duration_us);
```

#### Modem and Line Status

```
int32_t // UART_ERROR|UART_STATUS
uartGetLineStatus(int32_t handle);

int32_t // UART_ERROR|UART_SIGNAL
uartGetSignals(int32_t handle);

int32_t // UART_ERROR|void
uartSetSignals(int32_t handle, int32_t set_mask, int32_t clear_mask);
```

## Example - Writing and Reading from UART (C)

```
#include <stdint.h>
#include <stdio.h>

#include "brawl-device-uart.h"

static void uart_err(int32_t rc, const char *what) {
    if (rc < 0) fprintf(stderr, "%s failed: %d\n", what, rc);
}

#define MAX_UARTS 8

int main(void) {

    int32_t     uart_h, rc, n;
    uint8_t     buses[MAX_UARTS];
    const char *tx = "hello, uart!\r\n";
    uint8_t     rx[64];

    // enumerate UARTs (WASM MVP: caller-allocated buffer)
    n = uartCount();
    if (n < 0) { uart_err(n, "uartCount"); return 1; }
    if (n > MAX_UARTS) n = MAX_UARTS;

    rc = uartList((int32_t)(uintptr_t)buses, n);
    if (rc < 0) { uart_err(rc, "uartList"); return 1; }

    // do we have any UART's?
    if (rc > 0) {

         // open first UART
         uart_h = uartOpen(buses[0]);
         if (uart_h < 0) { uart_err(uart_h, "uartOpen"); return 1; }

         // configure: 115200 baud, 8N1, no flow; set sane timeouts/buffers
         rc = uartSetBaud(uart_h, 115200);
         if (rc < 0) { uart_err(rc, "uartSetBaud"); goto done; }
         rc = uartSetFormat(uart_h, 8, UART_PARITY_NONE, UART_STOPBITS_1);
         if (rc < 0) { uart_err(rc, "uartSetFormat"); goto done; }
         rc = uartSetFlow(uart_h, UART_FLOW_OFF);
         if (rc < 0) { uart_err(rc, "uartSetFlow"); goto done; }

         // configure per-transfer timeout default and buffer sizes
         rc = uartSetTimeouts(uart_h, 20000, 20000); // 20 ms RX/TX
         if (rc < 0) { uart_err(rc, "uartSetTimeouts"); goto done; }
         rc = uartSetBuffers(uart_h, 1024, 1024);
         if (rc < 0) { uart_err(rc, "uartSetBuffers"); goto done; }

         // write a line
         rc = uartWrite(uart_h, (int32_t)(uintptr_t)tx, (int32_t)strlen(tx), 20000);
         if (rc < 0) { uart_err(rc, "uartWrite"); goto done; }

         // try to read any echo/response (up to 64 bytes, 50 ms timeout)
         rc = uartRead(uart_h, (int32_t)(uintptr_t)rx, (int32_t)sizeof(rx), 50000);
         if (rc < 0) { uart_err(rc, "uartRead"); }

         // check sticky line status (clears on read)
         rc = uartGetLineStatus(uart_h);
         if (rc < 0) { uart_err(rc, "uartGetLineStatus"); }

done:

         // close
         rc = uartClose(uart_h);
         if (rc < 0) { uart_err(rc, "uartClose"); return 1; }
    }

    return 0;
}
```

#### Revision

2025-10 (draft)
