msrt-ffi 0.1.0

C ABI bindings for MSRT.
Documentation
  • Coverage
  • 100%
    58 out of 58 items documented0 out of 0 items with examples
  • Size
  • Source code size: 41.04 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 752.41 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 3s Average build duration of successful builds.
  • all releases: 3s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • atlas-orien/msrt-adapters
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • atlas-orien

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.

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:

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:

cargo build -p msrt-ffi --release

Outputs:

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

Example macOS link command:

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

Example Linux link command:

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:

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:

arm-none-eabi-gcc -DMSRT_FFI_NO_HEAP ...

Output:

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:

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:

#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:

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:

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

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.

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:

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

Poll until idle. MSRT returns one action per call.

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

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

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.