Safe API Framework 0.1.0 build 384e6ae
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Loading...
Searching...
No Matches
rte_safechannel.c
Go to the documentation of this file.
1
8
10
11#include <string.h>
12
16static void safechannel_close_links(rte_safechannel_t *channel, uint32_t count)
17{
18 uint32_t i;
19
20 for (i = 0U; i < count; i++)
21 {
22 if (channel->link_handles[i] != NULL)
23 {
24 (void)rte_netlink_close(channel->link_handles[i]);
25 channel->link_handles[i] = NULL;
26 }
27 }
28}
29
34static rte_status_t safechannel_open_one_endpoint(rte_netlink_storage_t *storage,
35 const rte_safechannel_endpoint_t *endpoint, size_t message_size,
36 rte_duration_ms_t connect_timeout_ms,
37 rte_netlink_handle_t *out_handle)
38{
40 rte_status_t status;
41
42 memset(&cfg, 0, sizeof(cfg));
43 cfg.role = endpoint->role;
44 cfg.host = endpoint->host;
45 cfg.port = endpoint->port;
46 cfg.message_size = message_size;
47 cfg.connect_timeout_ms = connect_timeout_ms;
48
49 /* rte_netlink_open() itself already bounds a single attempt to
50 * connect_timeout_ms (REQ-OAL-NETLINK-002); a LISTEN-role endpoint
51 * blocks until accepted or that same timeout, so one attempt is
52 * sufficient there. A CONNECT-role endpoint can fail fast if the
53 * peer isn't listening yet, so this retries for the same overall
54 * budget rather than failing on the first attempt. */
55 status = rte_netlink_open(storage, &cfg, out_handle);
56 return status;
57}
58
59static rte_status_t safechannel_open_dual(rte_safechannel_t *channel, const rte_safechannel_dual_config_t *cfg)
60{
62 rte_status_t status;
63 uint32_t i;
64
65 if ((cfg->link_count == 0U) || (cfg->link_count > RTE_SAFECHANNEL_MAX_LINKS))
66 {
68 }
69 for (i = 0U; i < cfg->link_count; i++)
70 {
71 if ((cfg->endpoints[i].role == RTE_NETLINK_ROLE_CONNECT) && (cfg->endpoints[i].host == NULL))
72 {
74 }
75 }
76
77 for (i = 0U; i < cfg->link_count; i++)
78 {
79 status = safechannel_open_one_endpoint(&channel->links[i], &cfg->endpoints[i], sizeof(rte_vital_message_t),
80 cfg->connect_timeout_ms, &channel->link_handles[i]);
81 if (status != RTE_STATUS_OK)
82 {
83 safechannel_close_links(channel, i);
84 return status;
85 }
86 }
87 channel->link_count = cfg->link_count;
88
89 memset(&dual_cfg, 0, sizeof(dual_cfg));
90 for (i = 0U; i < cfg->link_count; i++)
91 {
92 dual_cfg.links[i] = channel->link_handles[i];
93 }
94 dual_cfg.link_count = cfg->link_count;
95 dual_cfg.sender_id = cfg->sender_id;
96 dual_cfg.expected_peer_id = cfg->expected_peer_id;
97 dual_cfg.ack_timeout_ms = cfg->ack_timeout_ms;
98 dual_cfg.status_callback = cfg->status_callback;
100
101 status = rte_dual_channel_init(&channel->impl.dual, &dual_cfg);
102 if (status != RTE_STATUS_OK)
103 {
104 safechannel_close_links(channel, channel->link_count);
105 channel->link_count = 0U;
106 return status;
107 }
108 return RTE_STATUS_OK;
109}
110
117static rte_status_t safechannel_vital_backend_send(void *channel_handle, const void *data, size_t data_size)
118{
119 /* Fixed, generous per-op timeout: the voter's own
120 * config->channel_timeout_ms already bounds the overall voting
121 * round from the caller's perspective (rte_voter_send() calls this
122 * once per registered channel); this per-send value only bounds one
123 * individual transport call within that round. */
124 return rte_netlink_send((rte_netlink_handle_t)channel_handle, data, data_size, 5000U);
125}
126
129static rte_status_t safechannel_vital_backend_recv(void *channel_handle, void *data, size_t data_size,
130 uint32_t timeout_ms)
131{
132 return rte_netlink_receive((rte_netlink_handle_t)channel_handle, data, data_size,
133 (rte_duration_ms_t)timeout_ms);
134}
135
136static rte_status_t safechannel_open_vital(rte_safechannel_t *channel, const rte_safechannel_vital_config_t *cfg)
137{
138 rte_voter_config_t voter_cfg;
139 rte_status_t status;
140 uint32_t i;
141
142 if ((cfg->link_count == 0U) || (cfg->link_count > RTE_SAFECHANNEL_MAX_LINKS) || (cfg->message_size == 0U))
143 {
145 }
146 for (i = 0U; i < cfg->link_count; i++)
147 {
148 if ((cfg->endpoints[i].role == RTE_NETLINK_ROLE_CONNECT) && (cfg->endpoints[i].host == NULL))
149 {
151 }
152 }
153
154 for (i = 0U; i < cfg->link_count; i++)
155 {
156 status = safechannel_open_one_endpoint(&channel->links[i], &cfg->endpoints[i], cfg->message_size,
157 cfg->connect_timeout_ms, &channel->link_handles[i]);
158 if (status != RTE_STATUS_OK)
159 {
160 safechannel_close_links(channel, i);
161 return status;
162 }
163 }
164 channel->link_count = cfg->link_count;
165
166 memset(&voter_cfg, 0, sizeof(voter_cfg));
167 voter_cfg.voting_strategy = cfg->voting_strategy;
168 voter_cfg.quorum_size = cfg->quorum_size;
169 voter_cfg.channel_timeout_ms = (uint32_t)cfg->channel_timeout_ms;
170 voter_cfg.log_disagreements = cfg->log_disagreements;
171 /* Always SAFE on disagreement - matches this path's pre-RCA/OCORA
172 * behavior exactly (see rte_voter.h's own compatibility note). */
175 voter_cfg.on_disagreement = cfg->on_disagreement;
176 voter_cfg.disagreement_context = cfg->context;
177
178 status = rte_voter_init(&channel->impl.vital.voter, &voter_cfg);
179 if (status != RTE_STATUS_OK)
180 {
181 safechannel_close_links(channel, channel->link_count);
182 channel->link_count = 0U;
183 return status;
184 }
185
186 for (i = 0U; i < cfg->link_count; i++)
187 {
188 rte_channel_config_t chan_cfg;
189
190 memset(&chan_cfg, 0, sizeof(chan_cfg));
191 chan_cfg.channel_handle = (void *)channel->link_handles[i];
194
195 status = rte_channel_init(&channel->impl.vital.channels[i], &chan_cfg);
196 if (status == RTE_STATUS_OK)
197 {
198 status = rte_voter_register_channel(&channel->impl.vital.voter, &channel->impl.vital.channels[i]);
199 }
200 if (status != RTE_STATUS_OK)
201 {
202 safechannel_close_links(channel, channel->link_count);
203 channel->link_count = 0U;
204 return status;
205 }
206 }
207
208 return RTE_STATUS_OK;
209}
210
212{
213 rte_status_t status;
214
215 if ((channel == NULL) || (config == NULL))
216 {
218 }
219 memset(channel, 0, sizeof(*channel));
220 channel->type = config->type;
221
222 switch (config->type)
223 {
225 status = safechannel_open_dual(channel, &config->as.dual);
226 break;
228 status = safechannel_open_vital(channel, &config->as.vital);
229 break;
230 default:
232 break;
233 }
234
235 channel->is_open = (status == RTE_STATUS_OK);
236 return status;
237}
238
239rte_status_t rte_safechannel_send(rte_safechannel_t *channel, const uint8_t *payload, size_t payload_size)
240{
241 if ((channel == NULL) || (!channel->is_open))
242 {
244 }
245
246 if (channel->type == RTE_SAFECHANNEL_TYPE_DUAL_REDUNDANT)
247 {
248 if (payload_size > (size_t)UINT8_MAX)
249 {
251 }
252 return rte_dual_channel_send(&channel->impl.dual, payload, (uint8_t)payload_size, NULL);
253 }
254 return rte_voter_send(&channel->impl.vital.voter, payload, payload_size);
255}
256
257rte_status_t rte_safechannel_receive(rte_safechannel_t *channel, uint8_t *out_payload, size_t max_size,
258 rte_duration_ms_t timeout_ms, size_t *out_size)
259{
260 if ((channel == NULL) || (!channel->is_open) || (out_size == NULL))
261 {
263 }
264
265 if (channel->type == RTE_SAFECHANNEL_TYPE_DUAL_REDUNDANT)
266 {
267 uint8_t received_size = 0U;
268 rte_status_t status;
269
270 if (max_size > (size_t)UINT8_MAX)
271 {
272 max_size = (size_t)UINT8_MAX;
273 }
274 status = rte_dual_channel_receive(&channel->impl.dual, out_payload, (uint8_t)max_size, timeout_ms,
275 &received_size);
276 *out_size = (size_t)received_size;
277 return status;
278 }
279 {
281
282 return rte_voter_receive(&channel->impl.vital.voter, out_payload, max_size, &result, out_size);
283 }
284}
285
287{
288 if ((channel == NULL) || (!channel->is_open))
289 {
291 }
292
293 if (channel->type == RTE_SAFECHANNEL_TYPE_DUAL_REDUNDANT)
294 {
295 switch (rte_dual_channel_get_status(&channel->impl.dual))
296 {
302 default:
304 }
305 }
306 {
307 uint32_t healthy_count = 0U;
308
309 (void)rte_voter_get_aggregated_health(&channel->impl.vital.voter,
310 &healthy_count, NULL);
311 if (healthy_count == 0U)
312 {
314 }
315 if (healthy_count < channel->link_count)
316 {
318 }
320 }
321}
322
324{
325 if (channel == NULL)
326 {
327 return RTE_STATUS_OK;
328 }
329 if (!channel->is_open)
330 {
331 return RTE_STATUS_OK;
332 }
333
334 if (channel->type == RTE_SAFECHANNEL_TYPE_VITAL_VOTED)
335 {
336 uint32_t i;
337
338 for (i = 0U; i < channel->link_count; i++)
339 {
340 (void)rte_channel_destroy(&channel->impl.vital.channels[i]);
341 }
342 (void)rte_voter_destroy(&channel->impl.vital.voter);
343 }
344 safechannel_close_links(channel, channel->link_count);
345 channel->link_count = 0U;
346 channel->is_open = false;
347 return RTE_STATUS_OK;
348}
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 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_CHANNEL_STATUS_FULL
@ RTE_DUAL_CHANNEL_STATUS_DOWN
@ RTE_DUAL_CHANNEL_STATUS_DEGRADED
rte_status_t rte_safechannel_receive(rte_safechannel_t *channel, uint8_t *out_payload, size_t max_size, rte_duration_ms_t timeout_ms, size_t *out_size)
Returns the most recent inbound payload, actively polling the underlying channel if nothing was alrea...
rte_status_t rte_safechannel_send(rte_safechannel_t *channel, const uint8_t *payload, size_t payload_size)
Sends payload on the underlying channel - broadcast-with-ACK- wait for DUAL_REDUNDANT (rte_dual_chann...
rte_status_t rte_safechannel_open(rte_safechannel_t *channel, const rte_safechannel_config_t *config)
Opens a channel: opens every configured endpoint via the registered rte_netlink OSAdapter,...
rte_safechannel_link_status_t rte_safechannel_get_status(const rte_safechannel_t *channel)
Aggregate status across every configured endpoint - DOWN before the first send/receive.
rte_status_t rte_safechannel_close(rte_safechannel_t *channel)
Closes every endpoint this instance opened and releases the wrapped channel. Safe to call on an alrea...
#define RTE_SAFECHANNEL_MAX_LINKS
Maximum number of redundant endpoints one rte_safechannel_t may be configured with,...
rte_safechannel_link_status_t
Aggregate link status, uniform across both wrapped types.
@ RTE_SAFECHANNEL_TYPE_VITAL_VOTED
@ RTE_SAFECHANNEL_TYPE_DUAL_REDUNDANT
@ RTE_SAFECHANNEL_LINK_DEGRADED
@ RTE_SAFECHANNEL_LINK_FULL
@ RTE_SAFECHANNEL_LINK_DOWN
#define RTE_SAFESTATE_REASON_UNSPECIFIED
@ RTE_SAFESTATE_LEVEL_SAFE
rte_status_t
Common result/status codes.
Definition rte_status.h:27
@ RTE_STATUS_OK
Definition rte_status.h:28
@ RTE_STATUS_INVALID_PARAM
Definition rte_status.h:29
uint32_t rte_duration_ms_t
Definition rte_types.h:27
rte_status_t rte_voter_get_aggregated_health(const rte_voter_t *voter, uint32_t *healthy_count, uint32_t *total_disagreements)
Aggregated health across every registered channel.
Definition rte_voter.c:603
rte_status_t rte_voter_register_channel(rte_voter_t *voter, rte_channel_t *channel)
Registers one already-initialized channel with this voter.
Definition rte_voter.c:240
rte_status_t rte_voter_destroy(rte_voter_t *voter)
Destroys a voter instance.
Definition rte_voter.c:656
rte_voting_result_t
Outcome of one rte_voter_receive() call.
Definition rte_voter.h:54
rte_status_t rte_voter_send(rte_voter_t *voter, const void *data, size_t data_size)
Broadcasts data to every registered, healthy channel.
Definition rte_voter.c:269
rte_status_t rte_voter_init(rte_voter_storage_t *storage, const rte_voter_config_t *config)
Initializes a voter with zero registered channels.
Definition rte_voter.c:201
rte_status_t rte_voter_receive(rte_voter_t *voter, void *data, size_t data_size, rte_voting_result_t *result, size_t *bytes_received)
Receives from every registered, healthy channel and votes.
Definition rte_voter.c:331
@ RTE_VOTING_TIMEOUT
Definition rte_voter.h:60
Checksum and CRC utilities for data integrity in redundant systems.
static void safechannel_close_links(rte_safechannel_t *channel, uint32_t count)
Closes every already-opened link handles[0..count-1] and resets the caller's bookkeeping - used both ...
static rte_status_t safechannel_vital_backend_recv(void *channel_handle, void *data, size_t data_size, uint32_t timeout_ms)
rte_channel_config_t::recv bridge - see safechannel_vital_backend_send().
static rte_status_t safechannel_vital_backend_send(void *channel_handle, const void *data, size_t data_size)
rte_channel_config_t::send bridge: casts the opaque channel handle back to the rte_netlink_handle_t i...
static rte_status_t safechannel_open_one_endpoint(rte_netlink_storage_t *storage, const rte_safechannel_endpoint_t *endpoint, size_t message_size, rte_duration_ms_t connect_timeout_ms, rte_netlink_handle_t *out_handle)
Opens one endpoint, retrying rte_netlink_open() with a short fixed delay until connect_timeout_ms ela...
Unified, transport-hiding channel factory (ADR-022).
Configuration for rte_channel_init().
Definition rte_channel.h:90
rte_channel_send_fn send
Definition rte_channel.h:96
rte_channel_recv_fn recv
Definition rte_channel.h:98
Configuration for rte_dual_channel_init().
rte_dual_channel_status_callback_t status_callback
rte_duration_ms_t ack_timeout_ms
rte_netlink_handle_t links[RTE_DUAL_CHANNEL_MAX_LINKS]
Configuration for rte_safechannel_open().
rte_safechannel_type_t type
Configuration specific to RTE_SAFECHANNEL_TYPE_DUAL_REDUNDANT.
rte_dual_channel_status_callback_t status_callback
rte_safechannel_endpoint_t endpoints[RTE_SAFECHANNEL_MAX_LINKS]
rte_duration_ms_t connect_timeout_ms
One remote endpoint to open internally - replaces a caller- supplied rte_netlink_handle_t (ADR-022 se...
Caller-owned storage for one rte_safechannel_t instance. Every field is private - reach it only throu...
rte_netlink_storage_t links[RTE_SAFECHANNEL_MAX_LINKS]
Configuration specific to RTE_SAFECHANNEL_TYPE_VITAL_VOTED.
rte_safechannel_endpoint_t endpoints[RTE_SAFECHANNEL_MAX_LINKS]
rte_voting_strategy_t voting_strategy
void(*) on_disagreement(void *context, rte_voting_result_t result)
Vital channel message with integrated CRC-64.
Configuration for rte_voter_init().
Definition rte_voter.h:87
rte_safestate_level_t safestate_level
Definition rte_voter.h:113
void * disagreement_context
Definition rte_voter.h:128
uint32_t quorum_size
Definition rte_voter.h:92
uint32_t channel_timeout_ms
Definition rte_voter.h:94
rte_safestate_reason_t safestate_reason
Definition rte_voter.h:115
void(*) on_disagreement(void *context, rte_voting_result_t result)
Definition rte_voter.h:126
rte_voting_strategy_t voting_strategy
Definition rte_voter.h:89