AxlSerial — serial-port enumeration + line-setting readout

Serial-port enumeration via EFI_SERIAL_IO_PROTOCOL.

Header: <axl/axl-serial.h>. Two layers over EFI_SERIAL_IO_PROTOCOL: a read-only descriptor probe (enumerate handles, read each port’s line settings — baud, framing, timeout, FIFO depth — and modem control/status lines), and byte I/O for moving raw bytes over a chosen port. For console byte streams use <axl/axl-stream.h>; this is the lower-level per-port path (e.g. talking to a BMC’s SOL / a device on a specific UART).

Lazy on first call: AxlSerial locates the serial-I/O handles once with LocateHandleBuffer and caches the set for the image lifetime. On platforms with no serial ports every call returns NULL / AXL_ERR cleanly.

Cursor-style enumeration matches axl_block_next / axl_usb_next and returns the firmware AxlHandle directly, so position is recovered from the handle you pass back — no hidden shared cursor:

AxlHandle h = NULL;
while ((h = axl_serial_next(h)) != NULL) {
    AxlSerialMode m;
    AxlSerialControl c;
    if (axl_serial_get_mode(h, &m) == AXL_OK) {
        axl_printf("Uart(%u,%u,parity=%u,stop=%u)\n",
                   m.baud_rate, m.data_bits, m.parity, m.stop_bits);
    }
    if (axl_serial_get_control(h, &c) == AXL_OK && c.cts) {
        axl_printf("  CTS asserted\n");
    }
}

Byte I/O

To move bytes over a port, open an AxlSerial on one of the enumerated handles, optionally set the line mode, then read/write:

AxlSerial *s = NULL;
axl_serial_open(h, &s);                      // h from axl_serial_next
axl_serial_set_mode(s, &(AxlSerialMode){ .baud_rate = 115200,
                                         .data_bits = 8 });
size_t written = 0, got = 0;
axl_serial_write(s, "AT\r\n", 4, &written);
axl_serial_read(s, buf, sizeof buf, &got);   // non-blocking; got may be 0
axl_serial_close(s);

EFI_SERIAL_IO exposes no receive event, so for loop-driven input axl_serial_read_async(s, loop, poll_ms, cb, user) registers a timer that drains the port each tick and calls cb with whatever arrived (pick poll_ms to suit the UART rate; 5-10 ms for an interactive console). One async receive per port; axl_serial_close removes the source.

Device-path text needs no extra API: the same AxlHandle resolves through the existing axl_handle_get_protocol(h, "device-path", ...)

  • axl_device_path_to_text() (both in <axl/axl-sys.h>).

parity and stop_bits are raw enum codes the consumer names (e.g. "N", "8N1"); baud_rate is the firmware’s UINT64 BaudRate narrowed to 32 bits (every real UART rate fits).

API Reference

Serial-port enumeration and line-setting readout.

Enumerates the handles publishing the firmware’s serial-I/O protocol and reads each port’s line settings (baud, framing, timeout, FIFO depth) and modem control/status lines. This is a read-only descriptor probe for inventory and diagnostics — it does not open a port, transmit, or receive. (Console byte I/O is <axl/axl-stream.h>; this is the lower-level port descriptor.)

Cursor-style iteration matches the other platform readers and returns the firmware AxlHandle directly:

AxlHandle h = NULL;
while ((h = axl_serial_next(h)) != NULL) {
    AxlSerialMode m;
    if (axl_serial_get_mode(h, &m) == AXL_OK) {
        // ... report Uart(baud, data, parity, stop) ...
    }
}

Device-path text needs no new API: the same AxlHandle resolves through the existing axl_handle_get_protocol(h, "device-path", ...) + axl_device_path_to_text() (both in <axl/axl-sys.h>).

Line-setting fields are raw readouts; the consumer names the parity/stop-bit codes (e.g. “N”, “8N1”).

Typedefs

typedef struct AxlLoop AxlLoop
typedef struct AxlSerial AxlSerial

Opaque open serial port. Owns no firmware resource beyond the borrowed protocol pointer (the port is firmware-owned); axl_serial_close frees the wrapper and removes any async-read source.

typedef void (*AxlSerialReadFn)(const void *data, size_t len, void *user)

Async receive callback — invoked from the loop with the bytes read this poll tick (len > 0). data is owned by the library (valid only for the call); copy what you need.

Functions

AxlHandle axl_serial_next(AxlHandle prev)

Iterate handles publishing the serial-I/O protocol.

Cursor-style enumeration: pass NULL to get the first serial handle, then pass each returned handle back to get the next. Returns NULL once exhausted (including when no serial ports exist).

The handle set is located once and cached for the image lifetime (like AxlBlock / AxlUsb) — a port that appears afterward will not show up; the cache mirrors the boot device set. Position is recovered from the handle you pass back, not a hidden shared cursor: passing NULL — or any handle not in the cached set — starts again from the first port, and independent walks do not interfere. The returned handle is firmware-owned (do not free) and valid to pass to the readers below and to axl_handle_get_protocol(h, "device-path", ...).

Parameters:
  • prev – previous handle, or NULL to start

Returns:

next serial-I/O handle, or NULL at end of enumeration.

int axl_serial_get_mode(AxlHandle handle, AxlSerialMode *out)

Read a serial port’s current line settings.

Parameters:
  • handle – handle from axl_serial_next

  • out – [out] populated on success

Returns:

AXL_OK on success, AXL_ERR if handle does not publish the serial-I/O protocol or out is NULL.

int axl_serial_get_control(AxlHandle handle, AxlSerialControl *out)

Read a serial port’s modem control/status lines.

Calls the protocol’s GetControl and decodes the handshake bits.

Parameters:
  • handle – handle from axl_serial_next

  • out – [out] populated on success

Returns:

AXL_OK on success, AXL_ERR if handle does not publish the serial-I/O protocol, the GetControl call fails, or out is NULL.

int axl_serial_open(AxlHandle handle, AxlSerial **out)

Open a serial port for byte I/O.

handle is a serial-I/O handle from axl_serial_next. The port’s current line settings are left as-is (call axl_serial_set_mode to change them).

Parameters:
  • handle – handle from axl_serial_next

  • out – [out] open port on success

Returns:

AXL_OK with out set; AXL_ERR on NULL args, a handle that does not publish the serial-I/O protocol, or allocation failure.

void axl_serial_close(AxlSerial *s)

Close a serial port opened by axl_serial_open.

Removes any axl_serial_read_async source and frees the wrapper. Does not reset the underlying firmware port. NULL-safe.

Parameters:
  • s – port (NULL-safe)

int axl_serial_set_mode(AxlSerial *s, const AxlSerialMode *mode)

Set the port’s line settings (baud / framing / timeout).

Maps to the protocol’s SetAttributes. A zero field requests the device default (per the UEFI spec). mode uses the same field encoding as axl_serial_get_mode (parity / stop_bits raw codes).

Parameters:
  • s – open port

  • mode – desired settings

Returns:

AXL_OK on success; AXL_ERR on NULL args or a SetAttributes failure (e.g. an unsupported baud/framing combination).

int axl_serial_write(AxlSerial *s, const void *buf, size_t len, size_t *out_written)

Write bytes to the port (best-effort within the port’s timeout).

Writes up to len bytes; out_written (optional) receives the count actually transmitted. A short write (firmware timeout) is not an error — out_written tells the caller to retry the remainder.

Parameters:
  • s – open port

  • buf – bytes to send

  • len – number of bytes

  • out_written – [out] bytes transmitted (NULL = don’t care)

Returns:

AXL_OK on success (possibly short — see out_written); AXL_ERR on NULL s / buf or a device error.

int axl_serial_read(AxlSerial *s, void *buf, size_t cap, size_t *out_read)

Read available bytes from the port (non-blocking).

Returns immediately with whatever is buffered (up to cap). out_read receives the count — zero is a normal “nothing available right now” result, not an error. For a continuous receive, prefer axl_serial_read_async.

Parameters:
  • s – open port

  • buf – [out] receive buffer

  • cap – buffer capacity in bytes

  • out_read – [out] bytes read (0 = none available)

Returns:

AXL_OK (with out_read possibly 0); AXL_ERR on NULL s / buf / out_read, zero cap, or a device error.

int axl_serial_read_async(AxlSerial *s, AxlLoop *loop, size_t poll_ms, AxlSerialReadFn cb, void *user)

Start a loop-integrated receive: poll the port and deliver bytes.

Registers a poll_ms timer on loop; each tick drains the port and, if any bytes arrived, invokes cb with them. EFI_SERIAL_IO exposes no receive event, so this is a poll (not an interrupt) — pick poll_ms to suit the UART rate (e.g. 5-10 ms for an interactive console). Only one async receive per port; a second call replaces the first. The source is removed by axl_serial_close.

Parameters:
  • s – open port

  • loop – event loop

  • poll_ms – poll interval in milliseconds (> 0)

  • cb – receive callback

  • user – caller context

Returns:

AXL_OK on success; AXL_ERR on NULL args, zero poll_ms, or if the timer source could not be added.

struct AxlSerialMode
#include <axl-serial.h>

Line settings for a serial port.

Typed projection of the firmware’s SERIAL_IO_MODE (current attributes). parity and stop_bits are raw enum codes the consumer maps to names; the firmware fields are:

  • parity: 0 Default, 1 None, 2 Even, 3 Odd, 4 Mark, 5 Space

  • stop_bits: 0 Default, 1 OneStopBit, 2 OneFive, 3 Two

Public Members

uint32_t baud_rate

current baud rate; 0 = device’s designed speed (firmware field is UINT64, narrowed to 32 bits — every real UART rate fits)

uint32_t data_bits

data bits per character (5-8; 0 = device default)

uint8_t parity

parity code (EFI_PARITY_TYPE raw)

uint8_t stop_bits

stop-bits code (EFI_STOP_BITS_TYPE raw)

uint32_t timeout

receive/transmit timeout in microseconds (0 = device default)

uint32_t receive_fifo_depth

receive FIFO depth in bytes

struct AxlSerialControl
#include <axl-serial.h>

Modem control/status lines for a serial port.

Decoded from the protocol’s GetControl bitmask. These are the status/handshake lines a diagnostic view reports; the consumer formats them.

Public Members

bool cts

Clear To Send asserted.

bool dsr

Data Set Ready asserted.

bool ri

Ring Indicate asserted.

bool dcd

Data Carrier Detect asserted.

bool hw_flow_control

hardware flow control enabled