# Custom Transport

Bring-your-own backend (UART, USB-CDC, TCP, SPI, …). Use this when the built-in
SEGGER RTT transport does not fit your link.

The transport **contract** lives in [`inc/trax_transport.h`](../../inc/trax_transport.h)
— that header is where the `trax_transport_*()` prototypes and their per-function
`@brief` guidance are declared. It stays in `inc/` because the TraxProbe core is
compiled against it; this folder is just the intuitive landing spot for *writing*
a backend.

## The four functions you must define

You implement all four in **any `.c` in your build** — there is no ops struct, no
`register()` call, and no template to copy. The linker binds them by name.

```c
#include <trax_transport.h>

int    trax_transport_init(void);                      /* one-time arm; return 0 if nothing */
size_t trax_transport_write(const void *p, size_t n);  /* up-channel, all-or-nothing */
size_t trax_transport_read(void *p, size_t n);         /* down-channel, non-blocking */
void   trax_transport_clear_tx(void);                  /* drop stale TX on session restart */
```

**All four are required.** The core references every one, so leaving any undefined
fails the link with `undefined reference to 'trax_transport_…'` — never a silent
fake. `init()` is often just `return 0;`, but it stays mandatory because it is the
only hook where the RX path gets armed: defaulting it would silently kill the
down-channel instead of failing the build. `clear_tx()` is the same: session
restart always calls it. Empty any software TX ring you own (leave RX alone);
a blocking UART that cannot recall bytes already on the wire uses an empty
body. Do not hide this behind a config-macro name.

## What you no longer have to write

Earlier versions required six functions. Three of them are gone, and no existing
transport has to change to keep working:

| Was | Now |
|-----|-----|
| `trax_transport_has_data()` | Removed. The core drains with `read()` alone and treats a short read as "nothing left". |
| `trax_transport_bytes_used()` | Derived as capacity − free space. Diagnostics only. |
| `trax_transport_tx_free()` | Optional, opt-in via `TRAX_CFG_TRANSPORT_TX_FREE`. Recommended. |

Optional knobs, all named from `trax_config.h` and all defaulting to the behaviour
a transport had before they existed:

```c
#define TRAX_CFG_TRANSPORT_TX_FREE     my_tx_free       /* recommended */
#define TRAX_CFG_TRANSPORT_BUF_SIZE    sizeof(my_ring)  /* diagnostics  */
#define TRAX_CFG_TRANSPORT_BYTES_USED  my_tx_pending    /* diagnostics  */
```

## Steps

1. Set `#define TRAX_CFG_TRANSPORT TRAX_TRANSPORT_CUSTOM` in `trax_config.h`
   (this compiles the RTT backend out and reclaims its static buffers).
2. Define the four functions above in one of your application `.c` files.
   `trax_transport_custom.c.example` in this folder is a copy-paste starting
   point — rename it to a `.c`, drop it into your build, and fill in each
   function body for your hardware.
3. Honour the **all-or-nothing** write rule: `trax_transport_write()` must
   either commit every byte and return `size`, or accept nothing and return
   `0` (retryable backpressure — the core re-sends the batch next pass). A
   partial count is a fatal transport error that tears the stream down. See
   `.cursor/rules/trax-transport-transmission.mdc`.
4. Keep `trax_transport_read()` cheap and safe to call when idle. It runs on
   every `trax_process()` pass, and it is where per-poll housekeeping belongs
   (a non-blocking `accept()` on a socket backend, for instance) since there
   is no separate poll hook.
5. Point `TRAX_CFG_TRANSPORT_TX_FREE` at a free-space function whenever your
   link can report one (free bytes in your TX ring / DMA queue). The core
   sizes every drain batch to it before collecting frames, which keeps
   `trax_process()` passes cheap under backpressure and means `write()` never
   has to reject a batch. Skip it and the core assumes free space is unknown
   and falls back to try-and-retry — correct and lossless, but then
   `TRAX_CFG_TX_BATCH_MAX_BYTES` must stay below your TX buffer's real
   capacity or a max-size batch can never fit.

## Files

| File | Description |
|------|-------------|
| `README.md` | This guide |
| `trax_transport_custom.c.example` | Copy-paste skeleton defining the four required functions |

> **Switching back to RTT is safe.** The RTT backend defines `trax_transport_rtt_*()`
> (distinct names), so you can leave your custom-transport source in the build and
> just flip `TRAX_CFG_TRANSPORT` back to `TRAX_TRANSPORT_RTT` without a
> `multiple definition` error — in RTT mode your `trax_transport_*()` functions are
> simply unreferenced. (This de-conflicts only the transport functions; any
> ISR handlers / other symbols in that file are still compiled, so guard the file
> if those clash.)
