12#define SAI_MSG_EC_START 0x81U
13#define SAI_MSG_EC_DATA 0x86U
16#define SAI_HEADER_LEN 19U
17#define SAI_START_EXTRA_LEN 6U
19static void pack_u16_be(uint16_t v, uint8_t buf[2])
21 buf[0] = (uint8_t)((v >> 8U) & 0xFFU);
22 buf[1] = (uint8_t)(v & 0xFFU);
25static uint16_t unpack_u16_be(
const uint8_t buf[2])
27 return (uint16_t)(((uint16_t)buf[0] << 8U) | (uint16_t)buf[1]);
30static void pack_u32_be(uint32_t v, uint8_t buf[4])
32 buf[0] = (uint8_t)((v >> 24U) & 0xFFU);
33 buf[1] = (uint8_t)((v >> 16U) & 0xFFU);
34 buf[2] = (uint8_t)((v >> 8U) & 0xFFU);
35 buf[3] = (uint8_t)(v & 0xFFU);
38static uint32_t unpack_u32_be(
const uint8_t buf[4])
40 return ((uint32_t)buf[0] << 24U) | ((uint32_t)buf[1] << 16U) | ((uint32_t)buf[2] << 8U) | (uint32_t)buf[3];
44static int ec_state_from_delta(int32_t delta)
46 if (delta <= 0) {
return 0; }
47 if (delta == 1) {
return 1; }
48 if (delta == 2) {
return 2; }
55 if ((conn == NULL) || (saf == NULL) || (cfg == NULL) || (cfg->
ec_period_ms == 0U))
59 (void)memset(conn, 0,
sizeof(*conn));
80 uint8_t out[SAI_HEADER_LEN + SAI_START_EXTRA_LEN];
81 out[0] = SAI_MSG_EC_START;
82 pack_u16_be(conn->seq_tx, &out[1]);
83 (void)memset(&out[3], 0, 12U);
84 pack_u32_be(conn->ec_tx, &out[15]);
85 pack_u32_be(1U, &out[19]);
87 pack_u16_be((uint16_t)((period > 0xFFFFU) ? 0xFFFFU : period), &out[23]);
94 conn->seq_tx = (uint16_t)(conn->seq_tx + 1U);
103 if ((in_len != (SAI_HEADER_LEN + SAI_START_EXTRA_LEN)) || (in[0] != SAI_MSG_EC_START))
108 conn->last_rx_ec = unpack_u32_be(&in[15]);
109 conn->peer_ec_period_ms = unpack_u16_be(&in[23]);
110 if (conn->peer_ec_period_ms == 0U)
114 conn->seq_rx = unpack_u16_be(&in[1]);
115 conn->seq_rx_valid =
true;
121 conn->ec_initialized =
true;
129 if ((conn == NULL) || ((pdu == NULL) && (pdu_len > 0U)))
139 buf[0] = SAI_MSG_EC_DATA;
140 pack_u16_be(conn->seq_tx, &buf[1]);
141 (void)memset(&buf[3], 0, 12U);
142 pack_u32_be(conn->ec_tx, &buf[15]);
145 (void)memcpy(&buf[SAI_HEADER_LEN], pdu, pdu_len);
151 conn->seq_tx = (uint16_t)(conn->seq_tx + 1U);
158 if ((conn == NULL) || (out == NULL) || (out_len == NULL))
170 if ((in_len < SAI_HEADER_LEN) || (in[0] != SAI_MSG_EC_DATA))
176 uint16_t rx_seq = unpack_u16_be(&in[1]);
177 if (conn->seq_rx_valid)
179 uint16_t diff = (uint16_t)(rx_seq - conn->seq_rx);
185 conn->seq_rx = (uint16_t)(rx_seq + 1U);
186 conn->seq_rx_valid =
true;
189 conn->last_rx_ec = unpack_u32_be(&in[15]);
190 if (conn->ec_initialized)
192 int32_t delta = (int32_t)(conn->
expected_ec - (
double)conn->last_rx_ec);
193 conn->
ec_state = ec_state_from_delta(delta);
200 size_t payload_len = in_len - SAI_HEADER_LEN;
201 if (payload_len > out_cap)
205 if (payload_len > 0U)
207 (void)memcpy(out, &in[SAI_HEADER_LEN], payload_len);
209 *out_len = payload_len;
215 if ((conn == NULL) || (!conn->ec_initialized))
219 int32_t delta = (int32_t)(conn->
expected_ec - (
double)conn->last_rx_ec);
220 conn->
ec_state = ec_state_from_delta(delta);
safecomm_status_t safecomm_saf_send(safecomm_saf_conn_t *conn, const uint8_t *pdu, size_t pdu_len)
Send one user PDU as a MAC-protected, sequence-numbered Sa-DATA.
safecomm_status_t safecomm_saf_recv(safecomm_saf_conn_t *conn, uint8_t *out, size_t out_cap, size_t *out_len)
Receive and verify one Sa-DATA PDU (blocking).
#define SAFECOMM_SAF_MAX_PDU
Largest SaF user-data PDU this build accepts (bytes).
Safe Application Intermediate (SAI) sub-layer (Subset-098 v4.0.0 §5.4).
safecomm_status_t safecomm_sai_send(safecomm_sai_conn_t *conn, const uint8_t *pdu, size_t pdu_len)
Wrap one user PDU in a SAI header (message type, sequence number, our EC counter) and send it through...
safecomm_status_t safecomm_sai_recv(safecomm_sai_conn_t *conn, uint8_t *out, size_t out_cap, size_t *out_len)
Receive, unwrap and check one SAI-protected PDU.
safecomm_status_t safecomm_sai_init(safecomm_sai_conn_t *conn, safecomm_saf_conn_t *saf, const safecomm_sai_config_t *cfg)
Initialise a SAI connection object over an already-initialised SaF connection.
safecomm_status_t safecomm_sai_start(safecomm_sai_conn_t *conn)
Run the EC initialisation exchange (§5.4.9.3): send our ExecutionCycleStart, wait for the peer's,...
void safecomm_sai_tick(safecomm_sai_conn_t *conn)
Advance our own EC by one: call this once per cfg.ec_period_ms elapsed on the caller's clock....
safecomm_status_t
Result of a Safe Communication Framework operation.
@ SAFECOMM_STATUS_AUTH_FAILED
@ SAFECOMM_STATUS_INVALID_PARAM
@ SAFECOMM_STATUS_TIMEOUT
@ SAFECOMM_STATUS_SEQ_ERROR
@ SAFECOMM_STATUS_BUFFER_TOO_SMALL
SaF connection object. Caller-allocated, zero-initialised, opaque in practice — fields are not part o...
Static configuration for one SAI connection.
SAI connection object, layered over an already-initialised safecomm_saf_conn_t. Caller-allocated,...
safecomm_saf_conn_t * saf