msrt-ffi 0.1.0

C ABI bindings for MSRT.
Documentation
# msrt-ffi

C ABI bindings for MSRT.

This crate is intended to be shipped as source. C users build a target-specific
static library from this crate, then link that library into their C application
or MCU firmware.

```text
msrt_ffi.h is portable.
libmsrt_ffi.a is target-specific.
```

There is no universal `.a`, `.so`, or `.dylib` that works on every host and MCU.

## What The C Side Owns

`msrt-ffi` does not open serial ports, sockets, USB devices, or UART drivers.
The C application owns real IO:

```text
transport read bytes  -> msrt_endpoint_receive()
msrt_endpoint_poll()  -> write MSRT_POLL_TRANSMIT bytes to transport
MSRT_POLL_MESSAGE     -> deliver application payload
```

## Build For Host

Build a host static library:

```sh
cargo build -p msrt-ffi --release
```

Outputs:

```text
target/release/libmsrt_ffi.a
crates/msrt-ffi/include/msrt_ffi.h
```

Example macOS link command:

```sh
cc app.c \
  -I crates/msrt-ffi/include \
  target/release/libmsrt_ffi.a \
  -framework Security -framework CoreFoundation \
  -o app
```

Example Linux link command:

```sh
cc app.c \
  -I crates/msrt-ffi/include \
  target/release/libmsrt_ffi.a \
  -ldl -lpthread -lm \
  -o app
```

Native libraries vary by toolchain and target.

## Build For MCU

Build a no-std static library for a specific MCU target:

```sh
rustup target add thumbv7em-none-eabihf

cargo build -p msrt-ffi \
  --release \
  --target thumbv7em-none-eabihf \
  --no-default-features
```

When compiling C code against a no-heap MCU build, define `MSRT_FFI_NO_HEAP` so
the host-only `msrt_endpoint_new/free` declarations are hidden:

```sh
arm-none-eabi-gcc -DMSRT_FFI_NO_HEAP ...
```

Output:

```text
target/thumbv7em-none-eabihf/release/libmsrt_ffi.a
```

That `.a` is only for that target. A Cortex-M0, Cortex-M4 hard-float, RISC-V MCU,
Linux host, and macOS host each need their own build.

The MCU C firmware link step usually looks like:

```text
C application objects
+ vendor HAL/startup objects
+ target/<mcu-target>/release/libmsrt_ffi.a
+ linker script
-> firmware.elf
-> firmware.bin / firmware.hex
```

Use your MCU toolchain's linker flags for section garbage collection, for
example `-ffunction-sections`, `-fdata-sections`, and `-Wl,--gc-sections`.

## MCU-Friendly Storage API

The primary API does not require heap allocation. C owns endpoint storage:

```c
#include "msrt_ffi.h"

msrt_endpoint_storage_t storage;
msrt_endpoint_t *endpoint = NULL;

int32_t status = msrt_endpoint_init(&storage, NULL, &endpoint);
if (status != MSRT_STATUS_OK) {
    return 1;
}

/* Use endpoint. */

msrt_endpoint_deinit(endpoint);
```

`msrt_endpoint_storage_t` is opaque. Do not read or write its fields directly.

The header defines conservative storage for the endpoint. At runtime or startup,
you may compare:

```c
size_t need = msrt_endpoint_storage_size();
size_t align = msrt_endpoint_storage_align();
```

If the storage compiled into the header is not enough for this Rust build,
`msrt_endpoint_init()` returns `MSRT_STATUS_INVALID_STORAGE`.

## Host Convenience API

When built with default features, host programs may also use heap allocation:

```c
msrt_endpoint_t *endpoint = msrt_endpoint_new(NULL);
msrt_endpoint_free(endpoint);
```

This API is not available in `--no-default-features` MCU builds. Portable C code
should prefer `msrt_endpoint_init()` and `msrt_endpoint_deinit()`.

## Endpoint Roles

Use `MSRT_ROLE_CLIENT` for the active side. A client must call
`msrt_endpoint_connect()` before sending messages.

Use `MSRT_ROLE_PASSIVE` for a single-peer passive side, such as an MCU waiting
for a host. A passive endpoint creates its protocol session when bytes arrive,
so it does not call `msrt_endpoint_connect()`.

## Custom Configuration

```c
msrt_config_t config;
msrt_config_default(&config);

config.role = MSRT_ROLE_PASSIVE;
config.integrity = MSRT_INTEGRITY_CRC32;
config.retransmit_timeout_ms = 100;

msrt_endpoint_storage_t storage;
msrt_endpoint_t *endpoint = NULL;
msrt_endpoint_init(&storage, &config, &endpoint);
```

Both peers must use the same integrity setting.

## Driving The Endpoint

Call `receive` whenever bytes arrive. Input does not need to be packet-aligned.

```c
uint8_t rx[256];
size_t rx_len = transport_read(rx, sizeof(rx));

if (rx_len > 0) {
    msrt_receive_event_t receive_event;
    msrt_endpoint_receive(endpoint, now_ms, rx, rx_len, &receive_event);
}
```

Queue an application message:

```c
const uint8_t payload[] = "hello";
msrt_endpoint_send(endpoint, payload, sizeof(payload) - 1);
```

Poll until idle. MSRT returns one action per call.

```c
msrt_poll_event_t event;

while (msrt_endpoint_poll(endpoint, now_ms, &event) == MSRT_STATUS_OK) {
    if (event.kind == MSRT_POLL_IDLE) {
        break;
    }

    if (event.kind == MSRT_POLL_TRANSMIT) {
        transport_write(event.bytes, event.len);
    } else if (event.kind == MSRT_POLL_MESSAGE) {
        handle_message(event.bytes, event.len);
    } else if (event.kind == MSRT_POLL_SEND_FAILED) {
        msrt_endpoint_disconnect(endpoint);
        break;
    }
}
```

## Main Loop Shape

```c
while (running) {
    uint64_t now_ms = monotonic_milliseconds();

    uint8_t rx[256];
    size_t rx_len = transport_read(rx, sizeof(rx));
    if (rx_len > 0) {
        msrt_receive_event_t receive_event;
        msrt_endpoint_receive(endpoint, now_ms, rx, rx_len, &receive_event);
    }

    msrt_poll_event_t event;
    while (msrt_endpoint_poll(endpoint, now_ms, &event) == MSRT_STATUS_OK) {
        if (event.kind == MSRT_POLL_IDLE) {
            break;
        }
        if (event.kind == MSRT_POLL_TRANSMIT) {
            transport_write(event.bytes, event.len);
        }
        if (event.kind == MSRT_POLL_MESSAGE) {
            handle_message(event.bytes, event.len);
        }
    }
}
```

## Status Codes

```text
MSRT_STATUS_OK              success
MSRT_STATUS_NULL            required pointer was NULL
MSRT_STATUS_INVALID_SLICE   byte pointer was NULL while length was non-zero
MSRT_STATUS_PROTOCOL_ERROR  MSRT rejected or could not process input
MSRT_STATUS_UNSUPPORTED     unsupported role or option
MSRT_STATUS_NO_SESSION      send was requested without an active session
MSRT_STATUS_INVALID_STORAGE storage is too small or incorrectly aligned
```

## Example

`examples/loopback.c` shows a small in-process client/passive exchange. It feeds
`MSRT_POLL_TRANSMIT` bytes from one endpoint into the other endpoint. In a real
program, replace that transfer with UART, USB CDC, socket, or another byte
transport.