ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_c6link_internal.h File Reference

Cross-translation-unit contract inside libs/ra8_c6link. More...

#include <stddef.h>
#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_c6link.h"
#include "ra8_err.h"
#include "esp_hosted_header.h"
#include "esp_hosted_interface.h"
#include "esp_hosted_rpc.pb-c.h"
#include "esp_hosted_transport.h"
#include "protobuf-c/protobuf-c.h"
Include dependency graph for ra8_c6link_internal.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

 Where the payload of a classified frame is, and what it claims to be. More...
 What an answer extractor is given alongside the decoded message. More...

Typedefs

typedef struct ra8_c6link_rx_view ra8_c6link_rx_view_t
typedef struct ra8_c6link_take_ctx ra8_c6link_take_ctx_t

Enumerations

enum  ra8_c6link_frame_class_t : uint8_t {
  k_ra8_c6link_frame_data = 0U ,
  k_ra8_c6link_frame_idle = 1U ,
  k_ra8_c6link_frame_malformed = 2U ,
  k_ra8_c6link_frame_bad_checksum = 3U
}
 What one received transaction turned out to be. More...
enum  ra8_c6link_caps_t : uint8_t {
  k_ra8_c6link_caps_type = 0U ,
  k_ra8_c6link_caps_len = 1U ,
  k_ra8_c6link_caps_hdr = 2U ,
  k_ra8_c6link_caps_tags = 5U ,
  k_ra8_c6link_caps_value_len = 1U ,
  k_ra8_c6link_caps_stride = 3U ,
  k_ra8_c6link_caps_bytes = 17U ,
  k_ra8_c6link_caps_host = 0U ,
  k_ra8_c6link_caps_chip = 0x0DU ,
  k_ra8_c6link_caps_raw_tp = 0U ,
  k_ra8_c6link_caps_throttle_high = 80U ,
  k_ra8_c6link_caps_throttle_low = 60U
}
 The host-capabilities announcement, tag by tag. More...
enum  ra8_c6link_tlv_t : uint16_t {
  k_ra8_c6link_tlv_t_epname = 0x01U ,
  k_ra8_c6link_tlv_t_data = 0x02U ,
  k_ra8_c6link_tlv_type = 0U ,
  k_ra8_c6link_tlv_len_lo = 1U ,
  k_ra8_c6link_tlv_len_hi = 2U ,
  k_ra8_c6link_tlv_value = 3U ,
  k_ra8_c6link_tlv_ep_len = (uint16_t)(sizeof(RPC_EP_NAME_RSP) - 1U) ,
  k_ra8_c6link_tlv_overhead ,
  k_ra8_c6link_tlv_shift = 8U ,
  k_ra8_c6link_tlv_mask = 0xFFU
}
 The serial endpoint's TLV envelope, tag by tag. More...

Functions

void * priv_c6link_arena_alloc (void *ctx, size_t size)
 Take a block from the link's decode arena.
void priv_c6link_arena_free (void *ctx, void *pointer)
 Return a block to the link's decode arena.
void priv_c6link_arena_reset (ra8_c6link_t *link)
 Empty the link's decode arena.
void priv_c6link_arena_bind (ProtobufCAllocator *out, ra8_c6link_t *link)
 Bind an allocator descriptor to a link's arena.
void priv_c6link_frame_filler (uint8_t *tx)
 Stamp the transmit transaction as the host's idle filler.
void priv_c6link_frame_seal (uint8_t *tx, uint8_t if_type, uint8_t if_num, uint16_t len)
 Wrap an already-staged payload in a payload header.
ra8_c6link_frame_class_t priv_c6link_frame_classify (uint8_t *rx, ra8_c6link_rx_view_t *view)
 Decide what a received transaction is, and where its payload lies.
uint8_t priv_c6link_caps (uint8_t *out, uint8_t cap)
 Build the host-capabilities announcement.
ra8_err_t priv_c6link_tlv_open (uint8_t *out, uint16_t cap, uint16_t proto_len, uint16_t *body_at)
 Write both envelope tags ahead of a protobuf payload.
const uint8_t * priv_c6link_tlv_body (const uint8_t *payload, uint16_t len, uint16_t *proto_len)
 Strip the envelope off a received serial payload.
ra8_err_t priv_c6link_take_resp (void *ctx, const void *msg_v)
 Extract the result code from an answer that carries nothing else.
ra8_err_t priv_c6link_bare_req (ra8_c6link_t *link, uint32_t req_id)
 Issue a request whose body carries no fields and whose answer carries only a result code.
ra8_err_t priv_c6link_rpc_call (ra8_c6link_t *link, Rpc *req, uint32_t resp_id, ra8_c6link_take_fn_t take, void *take_ctx)
 Issue one request and pump until its answer arrives.
bool priv_c6link_rpc_consume (ra8_c6link_t *link, const uint8_t *payload, uint16_t len)
 Decode one control-plane payload and act on it.
ra8_err_t priv_c6link_resp (ra8_c6link_t *link, uint32_t rpc_id, int32_t resp)
 Map a co-processor result code onto an ra8 error, recording it.
ra8_err_t priv_c6link_pump (ra8_c6link_t *link, uint16_t max_transactions, ra8_c6link_stats_t *stats)
 Clock transactions until the budget or the wait says stop.
bool priv_c6link_dispatch (ra8_c6link_t *link, const ra8_c6link_rx_view_t *view)
 Route one well-formed received frame to whatever understands it.
void priv_c6link_emit (ra8_c6link_t *link, const ra8_c6link_event_t *ev)
 Deliver one decoded announcement to the registered callback.
uint8_t priv_c6link_copy_str (char *dst, uint8_t cap, const ProtobufCBinaryData *src)
 Copy a length-counted binary field into a NUL-terminated string.
bool priv_c6link_copy_mac (ra8_c6link_mac_t *dst, const ProtobufCBinaryData *src)
 Copy a binary field into a MAC address, all-or-nothing.

Detailed Description

Cross-translation-unit contract inside libs/ra8_c6link.

Tag
[Ring 4 / PAL] {World: NS}

Not public API. Only the six ra8_c6link_*.c translation units include this, plus tests under tests/ driving MC/DC vectors against helpers that no public entry point can reach with the required argument combinations. Application code must use ra8_c6link.h and ra8_c6link_wifi.h.

This is also the only first-party header that names the vendored esp-hosted types. Keeping the includes here is what lets the public headers stay free of the esp-hosted include path.

Since
0.1.0

Definition in file ra8_c6link_internal.h.

Typedef Documentation

◆ ra8_c6link_rx_view_t

◆ ra8_c6link_take_ctx_t

Enumeration Type Documentation

◆ ra8_c6link_caps_t

enum ra8_c6link_caps_t : uint8_t

The host-capabilities announcement, tag by tag.

Byte for byte the privileged frame upstream's transport_drv.c composes in send_slave_config(): an ESP_PRIV_EVENT_INIT header followed by five LEGACY-OK: send_slave_config() is the upstream esp-hosted symbol name one-octet TLVs. Sending it is not optional politeness – the co-processor treats it as the host's arrival and (re)announces itself with Event_ESPInit in reply, and until it has been sent this co-processor build answers no RPC at all. That was measured: a facade that skipped it answered once, on a co-processor another application had already announced to, and then never again.

The three policy values are the host's own and are restated here rather than included, because the port headers that define them do not exist in the host test build. They match port_esp_hosted_host_config_features.h.

Invariant
k_ra8_c6link_caps_bytes is the exact frame length, so a capacity check against it is neither loose nor tight.
k_ra8_c6link_caps_throttle_high is strictly above k_ra8_c6link_caps_throttle_low, which the co-processor requires.
Example:
out[k_ra8_c6link_caps_type] = (uint8_t)ESP_PRIV_EVENT_INIT;
See also
priv_c6link_caps
Since
0.1.0
Enumerator
k_ra8_c6link_caps_type 

Offset of the event-type octet.

k_ra8_c6link_caps_len 

Offset of the event-length octet.

k_ra8_c6link_caps_hdr 

Octets before the first TLV.

k_ra8_c6link_caps_tags 

TLVs this host emits.

k_ra8_c6link_caps_value_len 

Value width of every tag emitted.

k_ra8_c6link_caps_stride 

Octets one one-valued TLV occupies.

k_ra8_c6link_caps_bytes 

Whole frame: two header octets plus five three-octet TLVs.

k_ra8_c6link_caps_host 

Host capability word.

Zero, as upstream's own host sends.

k_ra8_c6link_caps_chip 

ESP_PRIV_FIRMWARE_CHIP_ESP32C6: the part this board carries.

k_ra8_c6link_caps_raw_tp 

Raw-throughput test direction; disabled, matching H_TEST_RAW_TP_DIR.

k_ra8_c6link_caps_throttle_high 

Flow-control high-water mark, H_WIFI_TX_DATA_THROTTLE_HIGH_THRESHOLD.

k_ra8_c6link_caps_throttle_low 

Flow-control low-water mark, H_WIFI_TX_DATA_THROTTLE_LOW_THRESHOLD.

Definition at line 383 of file ra8_c6link_internal.h.

◆ ra8_c6link_frame_class_t

enum ra8_c6link_frame_class_t : uint8_t

What one received transaction turned out to be.

The order the classifier applies these in is upstream's, from process_spi_rx_buf(): zero length first (filler), then the header sanity tests, then the checksum. Judging a filler frame by the rules for a data frame is what once made a healthy link report a failure, so filler is its own verdict and not a malformed data frame.

Invariant
Exactly one class is returned per transaction.
Only k_ra8_c6link_frame_data means the payload may be read.
Example:
See also
priv_c6link_frame_classify
Since
0.1.0
Enumerator
k_ra8_c6link_frame_data 

A well-formed frame whose checksum verified; the payload is real.

k_ra8_c6link_frame_idle 

The co-processor's filler frame: zero length, and legitimately offset = 0, which is not a defect.

k_ra8_c6link_frame_malformed 

The offset was not the header size, or the length did not fit.

k_ra8_c6link_frame_bad_checksum 

The recomputed checksum disagreed with the transmitted one.

Definition at line 68 of file ra8_c6link_internal.h.

◆ ra8_c6link_tlv_t

enum ra8_c6link_tlv_t : uint16_t

The serial endpoint's TLV envelope, tag by tag.

Two tags, each a one-byte type followed by a little-endian 16-bit length: the endpoint name, then the protobuf payload. The endpoint-name length is taken from the vendored RPC_EP_NAME_RSP string rather than written down, because upstream requires both endpoint names to be the same length and its parser checks that.

Invariant
k_ra8_c6link_tlv_ep_len equals strlen(RPC_EP_NAME_RSP).
k_ra8_c6link_tlv_overhead is the exact envelope cost, so a capacity check against it is neither loose nor tight.
Example:
See also
priv_c6link_tlv_open
Since
0.1.0
Enumerator
k_ra8_c6link_tlv_t_epname 

Tag introducing the endpoint name.

k_ra8_c6link_tlv_t_data 

Tag introducing the protobuf payload.

k_ra8_c6link_tlv_type 

Offset of a tag's type byte.

k_ra8_c6link_tlv_len_lo 

Offset of a tag's length, low byte.

k_ra8_c6link_tlv_len_hi 

Offset of a tag's length, high byte.

k_ra8_c6link_tlv_value 

Offset of a tag's value.

k_ra8_c6link_tlv_ep_len 

Endpoint-name length; both endpoint names share it.

k_ra8_c6link_tlv_overhead 

Bytes the envelope costs on top of the protobuf payload.

k_ra8_c6link_tlv_shift 

Shift between the two length bytes.

k_ra8_c6link_tlv_mask 

Byte mask for a length byte.

Definition at line 464 of file ra8_c6link_internal.h.

Function Documentation

◆ priv_c6link_arena_alloc()

void * priv_c6link_arena_alloc ( void * ctx,
size_t size )

Take a block from the link's decode arena.

The alloc row of the ::ProtobufCAllocator handed to the generated codec. Bumps a pointer through the caller-supplied arena, eight-byte aligned. There is no fallback to a heap: this firmware has none, and an over-request must fail the decode rather than fault.

Parameters
[in]ctxThe ra8_c6link_t whose arena to draw from; must be non-null.
[in]sizeBytes requested; zero yields a non-null zero-length block, which is what protobuf-c expects.
Returns
Pointer to the block, or null when the arena cannot serve it.
Return values
NULLThe arena is exhausted, or ctx was null.
Precondition
The link is open, so its arena pointer and size are valid.
The caller releases through priv_c6link_arena_free.
Postcondition
The bump offset advanced by the aligned size, or nothing changed.
arena_last names this block when the call succeeded.
Note
Not thread-safe; one link, one pump, one decode at a time.
Example:
ProtobufCAllocator a = { .alloc = priv_c6link_arena_alloc, ... };
See also
priv_c6link_arena_reset
Since
0.1.0

Definition at line 67 of file ra8_c6link_arena.c.

References ra8_c6link::arena, ra8_c6link::arena_bytes, ra8_c6link::arena_last, ra8_c6link::arena_used, k_ra8_c6link_arena_mask, and RA8_PRIV.

Referenced by priv_c6link_arena_bind().

◆ priv_c6link_arena_bind()

void priv_c6link_arena_bind ( ProtobufCAllocator * out,
ra8_c6link_t * link )

Bind an allocator descriptor to a link's arena.

Fills the ::ProtobufCAllocator the generated codec is handed. Passing null to the codec instead would select protobuf-c's default allocator, which calls malloc; in this firmware _sbrk is a strong symbol that reports a fatal error, so that path faults rather than failing.

Parameters
[out]outDescriptor to fill; must be non-null.
[in]linkLink whose arena backs it; must be non-null.
Returns
Nothing.
Precondition
link is open.
out outlives every decode it is passed to.
Postcondition
Both rows and the context of out are set.
No link state is modified.
Note
Safe from any context; it only assigns.
Example:
ProtobufCAllocator a;
See also
priv_c6link_arena_alloc
Since
0.1.0

Definition at line 116 of file ra8_c6link_arena.c.

References priv_c6link_arena_alloc(), priv_c6link_arena_free(), and RA8_PRIV.

Referenced by internal_mdl_take_accepted(), internal_mdl_take_cancelled(), internal_mdl_take_chunk(), and priv_c6link_rpc_consume().

◆ priv_c6link_arena_free()

void priv_c6link_arena_free ( void * ctx,
void * pointer )

Return a block to the link's decode arena.

The free row of the allocator. A bump arena cannot free out of order, but it can free the newest block: when pointer is the most recent allocation the bump offset rolls back to it, which is what turns the codec's own unwind-on-error path into genuinely reclaimed space rather than waste. Any other pointer is retained until priv_c6link_arena_reset runs, which the RPC layer does after every decode.

Parameters
[in]ctxThe ra8_c6link_t whose arena owns the block; null is ignored.
[in]pointerBlock to release; null is ignored.
Returns
Nothing.
Precondition
pointer came from priv_c6link_arena_alloc on the same link.
No other reference to the block survives the call.
Postcondition
The bump offset is unchanged or rolled back to pointer.
No memory outside the arena is touched.
Note
Not thread-safe, for the same reason as the allocator.
Example:
See also
priv_c6link_arena_alloc
Since
0.1.0

Definition at line 94 of file ra8_c6link_arena.c.

References ra8_c6link::arena, ra8_c6link::arena_last, ra8_c6link::arena_used, and RA8_PRIV.

Referenced by priv_c6link_arena_bind().

◆ priv_c6link_arena_reset()

void priv_c6link_arena_reset ( ra8_c6link_t * link)

Empty the link's decode arena.

Called after every message is decoded and released, so each decode starts from a known offset and no leak can accumulate across messages. That is what bounds the arena requirement to one message rather than to a run.

Parameters
[in,out]linkLink whose arena to empty; null is ignored.
Returns
Nothing.
Precondition
No block from the arena is still referenced.
The link is open, or the call is a no-op.
Postcondition
The bump offset is zero.
The arena's bytes are left untouched, not scrubbed.
Note
Not thread-safe.
Example:
See also
priv_c6link_arena_alloc
Since
0.1.0

Definition at line 107 of file ra8_c6link_arena.c.

References ra8_c6link::arena_last, ra8_c6link::arena_used, and RA8_PRIV.

Referenced by priv_c6link_rpc_consume().

◆ priv_c6link_bare_req()

ra8_err_t priv_c6link_bare_req ( ra8_c6link_t * link,
uint32_t req_id )
nodiscard

Issue a request whose body carries no fields and whose answer carries only a result code.

Req_WifiStart, Req_WifiStop, Req_WifiDeinit, Req_WifiConnect and Req_WifiDisconnect are all this shape: an empty body, a result code back. They differ only in which generated initialiser and which pair of ids they name, so they share one implementation.

Parameters
[in,out]linkOpen handle; must be non-null.
[in]req_idRPC_ID__Req_* to send.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe co-processor reported success.
k_ra8_err_not_supportedreq_id is not one of the five.
k_ra8_err_timeoutNo answer arrived within the budget.
k_ra8_err_hw_timeoutThe co-processor never armed HANDSHAKE, so no transaction was clocked.
k_ra8_err_protocol_errorThe answer reported a failure.
k_ra8_err_spi_errorThe transport refused a transfer.
Precondition
link is open.
No other request is outstanding.
Postcondition
The link's fault slot reflects the outcome.
The wait slot is disarmed.
Note
Not thread-safe; it pumps.
Example:
return priv_c6link_bare_req(link, (uint32_t)RPC_ID__Req_WifiStart);
See also
priv_c6link_rpc_call
Since
0.1.0

Definition at line 263 of file ra8_c6link_wifi.c.

References internal_c6link_wifi_bare_body(), k_ra8_err_not_supported, k_ra8_err_null_ptr, priv_c6link_rpc_call(), priv_c6link_take_resp(), and RA8_PRIV.

Referenced by ra8_c6link_wifi_join(), ra8_c6link_wifi_leave(), ra8_c6link_wifi_start(), and ra8_c6link_wifi_stop().

◆ priv_c6link_caps()

uint8_t priv_c6link_caps ( uint8_t * out,
uint8_t cap )
nodiscard

Build the host-capabilities announcement.

Pure formatting: no hardware, no link state. Split out so the exact octets can be compared against the protocol in a host test rather than only against the code that wrote them.

Parameters
[out]outBuffer to fill; must be non-null.
[in]capOctets available at out.
Returns
The frame length in octets, or zero when it would not fit.
Return values
0out was null or cap is below k_ra8_c6link_caps_bytes.
Precondition
cap octets are writable at out.
The caller transmits the result on ESP_PRIV_IF, interface 0.
Postcondition
On success exactly k_ra8_c6link_caps_bytes octets were written.
On failure out is not modified.
Note
Safe from any context; it only writes constants.
Example:
const uint8_t n = priv_c6link_caps(&link->tx[k_ra8_c6link_header_bytes],
See also
ra8_c6link_await_ready
Since
0.1.0

Definition at line 194 of file ra8_c6link_frame.c.

References internal_c6link_caps_tlv(), k_ra8_c6link_caps_bytes, k_ra8_c6link_caps_chip, k_ra8_c6link_caps_hdr, k_ra8_c6link_caps_host, k_ra8_c6link_caps_len, k_ra8_c6link_caps_raw_tp, k_ra8_c6link_caps_stride, k_ra8_c6link_caps_tags, k_ra8_c6link_caps_throttle_high, k_ra8_c6link_caps_throttle_low, k_ra8_c6link_caps_type, and RA8_PRIV.

Referenced by ra8_c6link_await_ready().

◆ priv_c6link_copy_mac()

bool priv_c6link_copy_mac ( ra8_c6link_mac_t * dst,
const ProtobufCBinaryData * src )
nodiscard

Copy a binary field into a MAC address, all-or-nothing.

Parameters
[out]dstAddress to fill; must be non-null.
[in]srcBinary field from a decoded message; null clears dst.
Returns
true when the field held exactly k_ra8_c6link_mac_bytes octets.
Return values
truedst holds the address.
falseThe field was absent or the wrong length; dst is cleared.
Precondition
dst does not overlap src.
The caller treats a false return as a protocol failure, not a default.
Postcondition
dst is either fully written or fully cleared.
src is not modified.
Note
Pure copying; safe from any context.
Example:
(void)priv_c6link_copy_mac(&ev.bssid, &body->bssid);
See also
priv_c6link_copy_str
Since
0.1.0

Definition at line 56 of file ra8_c6link.c.

References k_ra8_c6link_mac_bytes, ra8_c6link_mac::octet, and RA8_PRIV.

Referenced by internal_c6link_rpc_ev_connected(), internal_c6link_rpc_ev_disconnected(), internal_c6link_take_ap(), and internal_c6link_take_mac().

◆ priv_c6link_copy_str()

uint8_t priv_c6link_copy_str ( char * dst,
uint8_t cap,
const ProtobufCBinaryData * src )

Copy a length-counted binary field into a NUL-terminated string.

Shared by the event decoder and the AP-record decoder. The co-processor supplies the bytes, so nothing about them is trusted: the copy is bounded by the destination and terminated whatever the source did.

Parameters
[out]dstDestination; must be non-null and cap bytes long.
[in]capBytes available at dst, including the terminator.
[in]srcBinary field from a decoded message; null copies nothing.
Returns
The number of octets copied, excluding the terminator.
Return values
0The field was absent, empty, or cap left no room.
Precondition
cap is at least one, so a terminator always fits.
dst does not overlap src.
Postcondition
dst is NUL-terminated.
At most cap - 1 octets were copied.
Note
Pure copying; safe from any context.
Example:
ev.ssid_len = priv_c6link_copy_str(ev.ssid, sizeof ev.ssid, &body->ssid);
See also
priv_c6link_copy_mac
Since
0.1.0

Definition at line 37 of file ra8_c6link.c.

References RA8_PRIV.

Referenced by internal_c6link_rpc_ev_connected(), internal_c6link_rpc_ev_disconnected(), internal_c6link_take_ap(), and internal_c6link_take_fw().

◆ priv_c6link_dispatch()

bool priv_c6link_dispatch ( ra8_c6link_t * link,
const ra8_c6link_rx_view_t * view )
nodiscard

Route one well-formed received frame to whatever understands it.

Control-plane frames go to the RPC decoder, station and access-point frames to the Ethernet receive callback, and everything else is counted. ESP_PRIV_IF frames are counted rather than decoded: this co-processor build transmits its only privileged frame with a checksum that does not match its own header (#529), so a conformant host never sees a valid one.

Parameters
[in,out]linkOpen handle; must be non-null.
[in]viewPayload location from the classifier; must be non-null.
Returns
true when the outstanding wait was satisfied and the pump should stop.
Return values
trueThe awaited answer arrived.
falseKeep clocking.
Precondition
The frame classified as data, so view is populated.
The link's receive transaction still holds the frame.
Postcondition
Exactly one consumer was offered the payload.
The running counters reflect where it went.
Note
Not thread-safe; runs inside the pump.
Example:
if (priv_c6link_dispatch(link, &view)) { break; }
See also
priv_c6link_pump
Since
0.1.0

Definition at line 87 of file ra8_c6link.c.

References ra8_c6link::cb_ctx, ra8_c6link_stats::eth_in, ra8_c6link_rx_view::if_type, ra8_c6link_rx_view::len, ra8_c6link_rx_view::offset, priv_c6link_rpc_consume(), RA8_PRIV, ra8_c6link::rx, ra8_c6link::rx_cb, ra8_c6link::stats, and ra8_c6link_stats::unrouted.

Referenced by internal_c6link_pump_receive().

◆ priv_c6link_emit()

void priv_c6link_emit ( ra8_c6link_t * link,
const ra8_c6link_event_t * ev )

Deliver one decoded announcement to the registered callback.

Lives in ra8_c6link.c beside the rest of the handle's state, and is called from the RPC decoder once it has turned an Event_* message into a first-party record.

Parameters
[in,out]linkOpen handle; must be non-null.
[in]evDecoded announcement; must be non-null.
Returns
Nothing.
Precondition
ev is fully populated for its kind.
The callback, if any, does not re-enter the link.
Postcondition
The announcement counter advanced.
boot_seen is set when ev is the boot announcement.
Note
Not thread-safe; runs inside the pump.
Example:
priv_c6link_emit(link, &ev);
See also
ra8_c6link_event_cb_t
Since
0.1.0

Definition at line 71 of file ra8_c6link.c.

References ra8_c6link::boot_seen, ra8_c6link::cb_ctx, ra8_c6link::event_cb, ra8_c6link_stats::events, k_ra8_c6link_event_boot, ra8_c6link_event::kind, RA8_PRIV, and ra8_c6link::stats.

Referenced by internal_c6link_rpc_event().

◆ priv_c6link_frame_classify()

ra8_c6link_frame_class_t priv_c6link_frame_classify ( uint8_t * rx,
ra8_c6link_rx_view_t * view )
nodiscard

Decide what a received transaction is, and where its payload lies.

Applies upstream's ordering, and verifies the checksum by zeroing the field in place and restoring it, so the value a caller later reports is the one the co-processor actually sent.

Parameters
[in,out]rxReceived transaction; must be non-null. Momentarily modified and restored.
[out]viewPayload location and interface, filled only for a data frame; must be non-null.
Returns
ra8_c6link_frame_class_t The verdict.
Return values
k_ra8_c6link_frame_dataThe payload is real and view describes it.
k_ra8_c6link_frame_idleThe co-processor had nothing to send.
k_ra8_c6link_frame_malformedThe header could not be believed.
k_ra8_c6link_frame_bad_checksumThe integrity check failed.
Precondition
The transfer has completed and rx is stable.
rx covers a whole transaction.
Postcondition
rx holds exactly the bytes it held on entry.
view is written only on k_ra8_c6link_frame_data.
Note
Not thread-safe against a concurrent read of rx.
Example:
See also
priv_c6link_frame_seal
Since
0.1.0
MC/DC:
The malformed test is a two-condition decision and the tests drive N+1 vectors against it; see tests/wireless/src/test_ra8_c6link.c.

Definition at line 227 of file ra8_c6link_frame.c.

References ra8_c6link_rx_view::if_num, ra8_c6link_rx_view::if_type, internal_c6link_frame_sum(), k_ra8_c6link_frame_bad_checksum, k_ra8_c6link_frame_data, k_ra8_c6link_frame_idle, k_ra8_c6link_frame_malformed, k_ra8_c6link_header_bytes, k_ra8_c6link_max_payload, ra8_c6link_rx_view::len, memcpy(), ra8_c6link_rx_view::offset, and RA8_PRIV.

Referenced by internal_c6link_pump_receive().

◆ priv_c6link_frame_filler()

void priv_c6link_frame_filler ( uint8_t * tx)

Stamp the transmit transaction as the host's idle filler.

Field for field what the reference host sends when it has nothing queued: if_type = ESP_MAX_IF and every other byte zero, checksum included. The co-processor needs a transaction to answer in, so an idle host still clocks one.

Parameters
[out]txTransmit transaction; must be non-null and k_ra8_c6link_frame_bytes long.
Returns
Nothing.
Precondition
No transfer is in flight on tx.
tx covers a whole transaction.
Postcondition
Byte zero carries ESP_MAX_IF; every other byte is zero.
The frame is byte-identical to upstream's dummy buffer.
Note
The clearing loop is bounded by k_ra8_c6link_frame_bytes (Rule 2).
Example:
See also
priv_c6link_frame_seal
Since
0.1.0

Definition at line 136 of file ra8_c6link_frame.c.

References internal_c6link_frame_clear(), k_ra8_c6link_hdr_nibble, memcpy(), and RA8_PRIV.

Referenced by priv_c6link_pump().

◆ priv_c6link_frame_seal()

void priv_c6link_frame_seal ( uint8_t * tx,
uint8_t if_type,
uint8_t if_num,
uint16_t len )

Wrap an already-staged payload in a payload header.

The payload is expected to be sitting at tx + k_ra8_c6link_header_bytes already – both the RPC encoder and the Ethernet transmit path write it there directly, so nothing is copied twice. This fills the header in front of it, zeroes the tail, and computes the checksum over header plus payload with the checksum field taken as zero, exactly as get_next_tx_buffer() does upstream.

Parameters
[in,out]txTransmit transaction; must be non-null.
[in]if_typeInterface type for the frame, 0..15.
[in]if_numInterface number for the frame, 0..15.
[in]lenPayload length already staged; at most k_ra8_c6link_max_payload.
Returns
Nothing.
Precondition
len bytes are staged at tx + k_ra8_c6link_header_bytes.
len is within k_ra8_c6link_max_payload.
Postcondition
The header describes the payload and the checksum covers both.
Every byte past the payload is zero.
Note
The tail-clearing loop is bounded by k_ra8_c6link_frame_bytes.
Example:
priv_c6link_frame_seal(link->tx, (uint8_t)ESP_SERIAL_IF, 0U, n);
See also
priv_c6link_frame_classify
Since
0.1.0

Definition at line 148 of file ra8_c6link_frame.c.

References internal_c6link_frame_clear(), k_ra8_c6link_hdr_nibble, k_ra8_c6link_hdr_seq, k_ra8_c6link_header_bytes, k_ra8_c6link_max_payload, memcpy(), and RA8_PRIV.

Referenced by priv_c6link_pump().

◆ priv_c6link_pump()

ra8_err_t priv_c6link_pump ( ra8_c6link_t * link,
uint16_t max_transactions,
ra8_c6link_stats_t * stats )
nodiscard

Clock transactions until the budget or the wait says stop.

The implementation behind ra8_c6link_poll, and the loop every request runs inside. Split out so the public entry point stays a validation wrapper and so tests can drive it against a co-processor model directly.

Parameters
[in,out]linkOpen handle; must be non-null.
[in]max_transactionsTransactions this call may clock; non-zero.
[out]statsCounters describing the run; must be non-null.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe pump ran to its budget or until the wait was satisfied.
k_ra8_err_hw_timeoutHANDSHAKE never went active.
k_ra8_err_spi_errorThe transport refused a transfer.
Precondition
link is open and its transport is bound.
stats has been cleared by the caller.
Postcondition
At most max_transactions transactions were clocked.
Any staged payload was transmitted, or is still staged.
Note
Not thread-safe; one pump at a time owns the transport.
Example:
(void)priv_c6link_pump(link, (uint16_t)k_ra8_c6link_rpc_transfers, &stats);
See also
priv_c6link_dispatch
Since
0.1.0

Definition at line 105 of file ra8_c6link_pump.c.

References ra8_c6link_transport::ctx, ra8_c6link_transport::delay_ms, ra8_c6link_stats::hs_timeouts, internal_c6link_pump_handshake(), internal_c6link_pump_receive(), k_ra8_c6link_frame_bytes, k_ra8_c6link_gap_ms, k_ra8_c6link_hs_giveup, k_ra8_err_hw_timeout, k_ra8_err_null_ptr, k_ra8_err_spi_error, k_ra8_ok, priv_c6link_frame_filler(), priv_c6link_frame_seal(), RA8_PRIV, ra8_c6link::rx, ra8_c6link::stats, ra8_c6link_transport::transfer, ra8_c6link_stats::transfers, ra8_c6link::transport, ra8_c6link::tx, ra8_c6link::tx_if, and ra8_c6link::tx_len.

Referenced by priv_c6link_rpc_call(), ra8_c6link_await_ready(), ra8_c6link_eth_send(), and ra8_c6link_poll().

◆ priv_c6link_resp()

ra8_err_t priv_c6link_resp ( ra8_c6link_t * link,
uint32_t rpc_id,
int32_t resp )
nodiscard

Map a co-processor result code onto an ra8 error, recording it.

Every Resp_* message carries an int32_t resp that is an esp_err_t on the far side. This preserves the general-purpose error's meaning in the RA8 domain while recording both the original value and request id in the fault slot. Unknown and component-specific values remain protocol errors, but the retained raw status still tells bring-up exactly what the C6 reported.

Parameters
[in,out]linkOpen handle; must be non-null.
[in]rpc_idRPC_ID__Req_* the answer belongs to.
[in]respThe co-processor's result code.
Returns
ra8_err_t Error code.
Return values
k_ra8_okresp was zero and the fault slot was cleared.
k_ra8_err_*A mapped general error, or protocol error for an unknown status; the fault slot names rpc_id and the exact raw resp.
Precondition
link is open.
resp came from a decoded answer, not from a default.
Postcondition
The fault slot reflects exactly this call.
No other link state is modified.
Note
Not thread-safe.
Example:
return priv_c6link_resp(link, RPC_ID__Req_WifiStart, body->resp);
See also
ra8_c6link_last_fault
Since
0.1.0

Definition at line 153 of file ra8_c6link_rpc.c.

References ra8_c6link::fault, internal_c6link_remote_error(), k_ra8_err_null_ptr, k_ra8_ok, RA8_PRIV, ra8_c6link_fault::resp, and ra8_c6link_fault::rpc_id.

Referenced by internal_c6link_take_ap(), internal_c6link_take_fw(), internal_c6link_take_mac(), internal_mdl_take_response(), and priv_c6link_take_resp().

◆ priv_c6link_rpc_call()

ra8_err_t priv_c6link_rpc_call ( ra8_c6link_t * link,
Rpc * req,
uint32_t resp_id,
ra8_c6link_take_fn_t take,
void * take_ctx )
nodiscard

Issue one request and pump until its answer arrives.

Assigns a fresh UID, packs req into the transmit transaction behind its TLV envelope, arms the link's wait slot, and pumps. Announcements and Ethernet frames that arrive meanwhile are dispatched normally; the pump stops as soon as the answer is extracted.

Parameters
[in,out]linkOpen handle; must be non-null.
[in,out]reqRequest to send; must be non-null and fully populated apart from its UID, which this call assigns.
[in]resp_idRPC_ID__Resp_* that answers req.
[in]takeExtractor for the answer's fields; must be non-null.
[in]take_ctxContext handed to take.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe answer arrived and take accepted it.
k_ra8_err_null_ptrAn argument was null.
k_ra8_err_not_initializedlink is not open.
k_ra8_err_busyA request is already outstanding.
k_ra8_err_invalid_sizeThe packed request does not fit a frame.
k_ra8_err_validation_failedThe codec packed a different number of bytes than it predicted.
k_ra8_err_timeoutNo answer arrived within the budget.
k_ra8_err_hw_timeoutThe co-processor never armed HANDSHAKE, so no transaction was clocked.
k_ra8_err_protocol_errortake rejected the answer.
k_ra8_err_spi_errorThe transport refused a transfer.
Precondition
The transport is up.
No other request is outstanding on link.
Postcondition
The wait slot is disarmed however the call ends.
On failure the link's last fault names req.
Note
Not thread-safe; it pumps.
Example:
(void)priv_c6link_rpc_call(link, &req, RPC_ID__Resp_WifiStart, take, &out);
See also
priv_c6link_rpc_consume
Since
0.1.0

Definition at line 168 of file ra8_c6link_rpc.c.

References ra8_c6link_wait::armed, ra8_c6link::fault, internal_c6link_rpc_stage(), k_ra8_c6link_rpc_transfers, k_ra8_err_busy, k_ra8_err_not_initialized, k_ra8_err_null_ptr, k_ra8_err_timeout, k_ra8_ok, ra8_c6link::next_uid, ra8_c6link::open, priv_c6link_pump(), RA8_PRIV, ra8_secure_memzero(), ra8_c6link_fault::resp, ra8_c6link_wait::result, ra8_c6link_fault::rpc_id, ra8_c6link_wait::satisfied, ra8_c6link::tx, ra8_c6link::tx_len, and ra8_c6link::wait.

Referenced by internal_c6link_sta_set_config(), internal_c6link_wifi_do_init(), internal_c6link_wifi_do_mode(), internal_mdl_call(), priv_c6link_bare_req(), ra8_c6link_fw_version(), ra8_c6link_wifi_ap_info(), and ra8_c6link_wifi_mac().

◆ priv_c6link_rpc_consume()

bool priv_c6link_rpc_consume ( ra8_c6link_t * link,
const uint8_t * payload,
uint16_t len )
nodiscard

Decode one control-plane payload and act on it.

Unwraps the envelope, decodes the message into the arena, then either satisfies the outstanding wait, delivers an announcement, or drops it. The arena is reset before returning however that goes.

Parameters
[in,out]linkOpen handle; must be non-null.
[in]payloadFrame payload; must be non-null.
[in]lenPayload length in bytes.
Returns
true when the outstanding wait was satisfied and the pump should stop.
Return values
trueThe awaited answer arrived and was extracted.
falseAnything else, including a delivered announcement.
Precondition
len bytes are readable at payload.
The link's arena is empty.
Postcondition
The arena is empty again.
At most one wait is satisfied per call.
Note
Not thread-safe; runs inside the pump.
Example:
if (priv_c6link_rpc_consume(link, &link->rx[view.offset], view.len)) { break; }
See also
priv_c6link_rpc_call
Since
0.1.0

Definition at line 359 of file ra8_c6link_rpc.c.

References internal_c6link_rpc_answer(), internal_c6link_rpc_event(), priv_c6link_arena_bind(), priv_c6link_arena_reset(), priv_c6link_tlv_body(), RA8_PRIV, ra8_c6link_stats::rpc_in, ra8_c6link::stats, and ra8_c6link_stats::undecodable.

Referenced by priv_c6link_dispatch().

◆ priv_c6link_take_resp()

ra8_err_t priv_c6link_take_resp ( void * ctx,
const void * msg_v )
nodiscard

Extract the result code from an answer that carries nothing else.

Most requests are answered by a message whose only field is resp. Which arm of the payload union holds it depends on the message id, so this switches on that rather than existing eight times over.

Parameters
[in]ctxA ra8_c6link_take_ctx_t naming the link and the request id.
[in]msg_vThe decoded Rpc; must be non-null.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe co-processor reported success.
k_ra8_err_protocol_errorIt reported a failure, or the answer was one this extractor does not know how to read.
Precondition
ctx names a live link.
msg_v is still owned by the decoder.
Postcondition
The link's fault slot reflects exactly this answer.
No other link state is modified.
Note
Runs inside the pump, on the polling thread.
Example:
ra8_c6link_take_ctx_t take = { .link = link, .rpc_id = RPC_ID__Req_WifiStart };
See also
priv_c6link_resp
Since
0.1.0

Definition at line 126 of file ra8_c6link_wifi.c.

References ra8_c6link_take_ctx::link, priv_c6link_resp(), RA8_PRIV, and ra8_c6link_take_ctx::rpc_id.

Referenced by internal_c6link_sta_set_config(), internal_c6link_wifi_do_init(), internal_c6link_wifi_do_mode(), and priv_c6link_bare_req().

◆ priv_c6link_tlv_body()

const uint8_t * priv_c6link_tlv_body ( const uint8_t * payload,
uint16_t len,
uint16_t * proto_len )
nodiscard

Strip the envelope off a received serial payload.

Both endpoint names are accepted, as upstream's parse_tlv() does: a response arrives on RPCRsp and an unsolicited event on RPCEvt, and the two are the same length by construction.

Parameters
[in]payloadFrame payload; must be non-null.
[in]lenPayload length in bytes.
[out]proto_lenProtobuf length found; must be non-null.
Returns
Pointer to the protobuf bytes, or null when the envelope is not one this endpoint recognises.
Return values
NULLThe tags, the endpoint name or the lengths did not check out.
Precondition
len bytes are readable at payload.
proto_len is writable.
Postcondition
On success the returned range lies wholly inside payload.
On failure proto_len is zero.
Note
Pure parsing; touches no hardware.
Example:
uint16_t n = 0U;
const uint8_t* proto = priv_c6link_tlv_body(payload, len, &n);
See also
priv_c6link_tlv_open
Since
0.1.0
MC/DC:
The tag test is a three-condition decision and the tests drive N+1 vectors against it; see tests/wireless/src/test_ra8_c6link.c.

Definition at line 146 of file ra8_c6link_tlv.c.

References internal_c6link_tlv_len(), internal_c6link_tlv_named(), k_ra8_c6link_tlv_ep_len, k_ra8_c6link_tlv_overhead, k_ra8_c6link_tlv_t_data, k_ra8_c6link_tlv_t_epname, k_ra8_c6link_tlv_type, and k_ra8_c6link_tlv_value.

Referenced by priv_c6link_rpc_consume().

◆ priv_c6link_tlv_open()

ra8_err_t priv_c6link_tlv_open ( uint8_t * out,
uint16_t cap,
uint16_t proto_len,
uint16_t * body_at )
nodiscard

Write both envelope tags ahead of a protobuf payload.

Parameters
[out]outBuffer to fill; must be non-null.
[in]capBytes available at out.
[in]proto_lenProtobuf length that will follow the envelope.
[out]body_atOffset the protobuf must be written at; must be non-null.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe envelope is written and body_at names its payload.
k_ra8_err_null_ptrout or body_at was null.
k_ra8_err_invalid_sizecap cannot hold envelope plus payload.
Precondition
cap bytes are writable at out.
proto_len is the exact packed size, not an estimate.
Postcondition
On success k_ra8_c6link_tlv_overhead bytes were written.
On failure out is not modified and body_at is zero.
Note
Pure formatting; touches no hardware.
Example:
uint16_t at = 0U;
(void)priv_c6link_tlv_open(buf, cap, packed, &at);
See also
priv_c6link_tlv_body
Since
0.1.0

Definition at line 91 of file ra8_c6link_tlv.c.

References internal_c6link_tlv_tag(), k_ra8_c6link_tlv_ep_len, k_ra8_c6link_tlv_overhead, k_ra8_c6link_tlv_t_data, k_ra8_c6link_tlv_t_epname, k_ra8_err_invalid_size, k_ra8_err_null_ptr, k_ra8_ok, and RA8_PRIV.

Referenced by internal_c6link_rpc_stage().