17#define RTE_DUAL_CHANNEL_STALL_POLL_LIMIT 32U
29 uint32_t up_count = 0U;
32 for (i = 0U; i < channel->link_count; i++)
34 if (channel->link_up[i])
44 if (up_count == channel->link_count)
55 if (new_status != channel->last_status)
59 channel->last_status = new_status;
60 if (channel->status_callback != NULL)
62 channel->status_callback(new_status, old_status, channel->status_callback_ctx);
78 uint8_t raw_size = 0U;
79 uint32_t sequence = 0U;
86 &raw_size, &sequence);
91 if (raw_size < (uint8_t)
sizeof(header))
98 (void)memcpy(&header, raw,
sizeof(header));
104 uint8_t app_size = (uint8_t)(raw_size - (uint8_t)
sizeof(header));
109 (void)memcpy(channel->pending_data, &raw[
sizeof(header)], app_size);
110 channel->pending_data_size = app_size;
111 channel->pending_data_valid =
true;
123 channel->ack_timeout_ms, NULL);
133 if (raw_size < (uint8_t)
sizeof(ack))
137 (void)memcpy(&ack, raw,
sizeof(ack));
150 (void)memcpy(&channel->pending_state, raw,
sizeof(channel->pending_state));
151 channel->pending_state_valid =
true;
168 channel->ack_timeout_ms, NULL);
187 if ((channel == NULL) || (config == NULL))
197 if (config->
links[i] == NULL)
212 channel->link_up[i] =
false;
216 channel->link_up[i] =
false;
224 channel->pending_data_valid =
false;
225 channel->pending_data_size = 0U;
226 channel->pending_state_valid =
false;
227 (void)memset(&channel->pending_state, 0,
sizeof(channel->pending_state));
240static bool dual_channel_send_to_link_and_wait_ack(
rte_dual_channel_t *channel, uint32_t i,
const uint8_t *frame,
241 uint8_t frame_size,
bool *saw_hard_fault,
244 uint32_t sent_sequence = 0U;
246 bool link_now_up =
false;
252 *saw_hard_fault =
true;
253 *hard_fault_status = send_status;
269 uint32_t stall_polls = 0U;
294 if (!*saw_hard_fault)
296 *saw_hard_fault =
true;
297 *hard_fault_status = poll_status;
308 if (now_ms > start_ms)
354 uint32_t *out_ack_link_count)
359 uint32_t ack_count = 0U;
369 bool saw_hard_fault =
false;
376 if ((payload == NULL) && (payload_size != 0U))
392 (void)memcpy(frame, &header,
sizeof(header));
393 if (payload_size > 0U)
395 (void)memcpy(&frame[
sizeof(header)], payload, payload_size);
397 frame_size = (uint8_t)(
sizeof(header) + payload_size);
400 for (i = 0U; i < channel->link_count; i++)
402 bool link_now_up = dual_channel_send_to_link_and_wait_ack(channel, i, frame, frame_size, &saw_hard_fault,
405 channel->link_up[i] = link_now_up;
412 if (out_ack_link_count != NULL)
414 *out_ack_link_count = ack_count;
417 dual_channel_update_status(channel);
429 if ((channel == NULL) || (out_payload == NULL) || (out_size == NULL) || (max_size == 0U))
435 bool saw_hard_fault =
false;
438 if (!channel->pending_data_valid)
441 rte_duration_ms_t per_link_timeout = (channel->link_count > 0U) ? (timeout_ms / channel->link_count) : 0U;
452 for (i = 0U; i < channel->link_count; i++)
466 saw_hard_fault =
true;
467 hard_fault_status = poll_status;
472 if (!channel->pending_data_valid)
477 if (channel->pending_data_size > max_size)
484 (void)memcpy(out_payload, channel->pending_data, channel->pending_data_size);
485 *out_size = channel->pending_data_size;
486 channel->pending_data_valid =
false;
495 uint32_t ack_count = 0U;
496 bool saw_hard_fault =
false;
516 (void)memset(&frame, 0,
sizeof(frame));
524 for (i = 0U; i < channel->link_count; i++)
526 bool link_now_up = dual_channel_send_to_link_and_wait_ack(
527 channel, i, (
const uint8_t *)&frame, (uint8_t)
sizeof(frame), &saw_hard_fault, &hard_fault_status);
529 channel->link_up[i] = link_now_up;
536 if (out_ack_link_count != NULL)
538 *out_ack_link_count = ack_count;
541 dual_channel_update_status(channel);
551 bool channel_degraded, uint64_t timestamp_ms)
555 uint32_t sent_count = 0U;
566 frame.
state = (uint8_t)state;
571 for (i = 0U; i < channel->link_count; i++)
574 (uint8_t)
sizeof(frame), channel->ack_timeout_ms, NULL);
587 if ((channel == NULL) || (out_frame == NULL))
592 if (!channel->pending_state_valid)
595 rte_duration_ms_t per_link_timeout = (channel->link_count > 0U) ? (timeout_ms / channel->link_count) : 0U;
599 for (i = 0U; i < channel->link_count; i++)
607 if (!channel->pending_state_valid)
612 *out_frame = channel->pending_state;
613 channel->pending_state_valid =
false;
624 return channel->last_status;
629 if ((channel == NULL) || (link_index >= channel->link_count))
633 return channel->link_up[link_index];
bool rte_dual_channel_is_link_up(const rte_dual_channel_t *channel, uint32_t link_index)
Returns whether one specific configured link is currently considered up, as of the most recent rte_du...
rte_status_t rte_dual_msgchannel_receive(rte_dual_msgchannel_t *channel, uint8_t *out_payload, uint8_t payload_max_size, rte_duration_ms_t timeout_ms, uint8_t *out_payload_size, uint32_t *out_sequence)
Receives one frame over config->link, blocking at most timeout_ms, verifies its CRC-64 and sequence c...
rte_status_t rte_dual_channel_receive_state_frame(rte_dual_channel_t *channel, rte_duration_ms_t timeout_ms, rte_dual_state_frame_t *out_frame)
Returns the most recently staged inbound STATE frame, actively polling the configured links if none w...
rte_status_t rte_dual_channel_send(rte_dual_channel_t *channel, const uint8_t *payload, uint8_t payload_size, uint32_t *out_ack_link_count)
Sends payload as a DATA frame on every configured redundant link - never gated by any negotiated stat...
rte_status_t rte_dual_channel_init(rte_dual_channel_t *channel, const rte_dual_channel_config_t *config)
Initializes a rte_dual_channel_t: initializes every configured redundant link (rte_dual_msgchannel_in...
rte_dual_channel_status_t
Aggregate connection status of a rte_dual_channel_t across all of its configured redundant links (ADR...
rte_status_t rte_dual_channel_send_heartbeat(rte_dual_channel_t *channel, uint32_t *out_ack_count)
Transmits a connection maintenance heartbeat frame across every configured link and waits up to ack_t...
rte_dual_state_t
State of one instance in a dual (two-redundant-instance) relationship, as decided by rte_dual_negotia...
#define RTE_DUAL_CHANNEL_MAX_PAYLOAD
Max application payload bytes usable via rte_dual_channel_send()/_receive() - Layer-1's own RTE_DUAL_...
#define RTE_DUAL_CHANNEL_MAX_LINKS
Maximum number of redundant links one rte_dual_channel_t may be configured with. Fixed,...
rte_dual_channel_status_t rte_dual_channel_get_status(const rte_dual_channel_t *channel)
Returns the aggregate connection status across every configured redundant link, as of the most recent...
rte_status_t rte_dual_channel_receive(rte_dual_channel_t *channel, uint8_t *out_payload, uint8_t max_size, rte_duration_ms_t timeout_ms, uint8_t *out_size)
Returns the most recently staged inbound DATA frame, actively polling the configured links (each auto...
rte_dual_frame_kind_t
Which kind of Layer-2 frame a given Layer-1 payload holds. Stored as the first byte of every rte_dual...
rte_status_t rte_dual_msgchannel_init(rte_dual_msgchannel_t *channel, const rte_dual_msgchannel_config_t *config)
Initializes a rte_dual_msgchannel_t: starts both sequence counters at 0.
rte_status_t rte_dual_channel_set_resync_on_sequence_error(rte_dual_channel_t *channel, bool enable)
#define RTE_DUAL_MSGCHANNEL_MAX_PAYLOAD
Max payload bytes usable via rte_dual_msgchannel_send()/ _receive() - bounded by rte_vital_message_t'...
rte_status_t rte_dual_msgchannel_set_resync_on_sequence_error(rte_dual_msgchannel_t *channel, bool enable)
Turns sequence resynchronisation on or off (default off after init).
rte_status_t rte_dual_msgchannel_send(rte_dual_msgchannel_t *channel, const uint8_t *payload, uint8_t payload_size, rte_duration_ms_t timeout_ms, uint32_t *out_sequence)
Wraps payload in a rte_vital_message_t (this channel's own sender_id and next sequence_number) and se...
rte_status_t rte_dual_channel_send_state_frame(rte_dual_channel_t *channel, rte_dual_state_t state, bool channel_degraded, uint64_t timestamp_ms)
Sends a rte_dual_state_frame_t (rte_dual_frames.h) on every configured redundant link - fire-and-forg...
@ RTE_DUAL_CHANNEL_STATUS_FULL
@ RTE_DUAL_CHANNEL_STATUS_DOWN
@ RTE_DUAL_CHANNEL_STATUS_DEGRADED
@ RTE_DUAL_FRAME_KIND_STATE
@ RTE_DUAL_FRAME_KIND_HEARTBEAT
@ RTE_DUAL_FRAME_KIND_ACK
@ RTE_DUAL_FRAME_KIND_DATA
rte_status_t
Common result/status codes.
@ RTE_STATUS_INVALID_PARAM
@ RTE_STATUS_DATA_CORRUPTION
@ RTE_STATUS_HARDWARE_FAULT
rte_status_t rte_timer_now(rte_timestamp_ms_t *out_now_ms)
Returns the current monotonic time base used by all timers.
uint32_t rte_duration_ms_t
uint64_t rte_timestamp_ms_t
static rte_status_t dual_channel_poll_link_once(rte_dual_channel_t *channel, uint32_t link_index, rte_duration_ms_t timeout_ms, dual_poll_result_t *out_result)
One receive attempt on one link, dispatched by frame kind: DATA is auto-ACKed and staged for rte_dual...
#define RTE_DUAL_CHANNEL_STALL_POLL_LIMIT
Cap on consecutive rte_dual_channel_send() ACK-wait polls that may complete without rte_timer_now() s...
"DualChannel" layer of ADR-020: wraps 1..N redundant rte_dual_msgchannel_t links for fault-tolerant,...
OS Abstraction Layer - Timer service.
Result of one dual_channel_poll_link_once() attempt.
rte_dual_frame_kind_t kind
RTE_DUAL_FRAME_KIND_ACK payload.
Configuration for rte_dual_channel_init().
uint32_t expected_peer_id
rte_dual_channel_status_callback_t status_callback
rte_duration_ms_t ack_timeout_ms
void * status_callback_ctx
rte_netlink_handle_t links[RTE_DUAL_CHANNEL_MAX_LINKS]
One rte_dual_channel_t instance's state. Caller-owned storage; every field is private - reach it only...
RTE_DUAL_FRAME_KIND_HEARTBEAT payload - connection maintenance heartbeat.
Configuration for rte_dual_msgchannel_init().
uint32_t expected_peer_id
rte_netlink_handle_t link
RTE_DUAL_FRAME_KIND_STATE payload - rte_dual_negotiator_t's own periodic beacon.