canard.h
40.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
/// __ __ _______ __ __ _______ _______ __ __
/// | | | | / _ ` | | | | / ____| / _ ` | ` | |
/// | | | | | |_| | | | | | | | | |_| | | `| |
/// | |_| | | _ | ` `_/ / | |____ | _ | | |` |
/// `_______/ |__| |__| `_____/ `_______| |__| |__| |__| `__|
/// | | | | | |
/// ----o------o------------o---------o------o---------o-------
///
/// Libcanard is a compact implementation of the UAVCAN/CAN protocol for high-integrity real-time embedded systems.
/// It is designed for use in robust deterministic embedded systems equipped with at least 32K ROM and 4..8K RAM.
/// The codebase follows the MISRA C rules, has 100% test coverage, and is validated by at least two static analyzers.
/// The library is designed to be compatible with any target platform and instruction set architecture, from 8 to 64
/// bit, little- and big-endian, RTOS-based or baremetal, etc., as long as there is a standards-compliant compiler.
///
/// INTEGRATION
///
/// The library is intended to be integrated into the end application by simply copying the file canard.c into the
/// source tree of the project; it does not require any special compilation options and should work out of the box.
/// There are optional build configuration macros defined near the top of canard.c; they may be used to fine-tune
/// the library for the target platform (but it is not necessary). This header file should be located in the same
/// directory with canard.c, or its location should be in the include look-up paths of the compiler.
///
/// As explained in this documentation, the library requires a deterministic constant-time bounded-fragmentation dynamic
/// memory allocator. If your target platform does not provide a deterministic memory manager (most platforms don't),
/// it is recommended to use O1Heap (MIT licensed): https://github.com/pavel-kirienko/o1heap.
///
/// There is an optional two-file extension library canard_dsdl.c + canard_dsdl.h which can be used alongside
/// this core library to simplify DSDL object serialization and deserialization. It is intended to be integrated in
/// the same manner. Please read its usage manual for further information.
///
/// There are no specific requirements to the underlying I/O layer. Some low-level drivers maintained by the
/// UAVCAN Development Team may be found at https://github.com/UAVCAN/platform_specific_components.
///
/// If your application requires a MISRA C compliance report, please get in touch with the maintainers via the forum
/// at https://forum.uavcan.org.
///
/// ARCHITECTURE
///
/// UAVCAN, as a protocol stack, is composed of two layers: TRANSPORT and PRESENTATION. The transport layer is portable
/// across different transport protocols, one of which is CAN (FD), formally referred to as UAVCAN/CAN. This library
/// is focused on UAVCAN/CAN only and it will not support other transports. The presentation layer is implemented
/// through the DSDL language and the associated data type regulation policies. Much like the UAVCAN stack itself,
/// this library consists of two major components:
///
/// 1. TRANSPORT -- the UAVCAN/CAN transport layer implementation. This is implemented in canard.c/.h,
/// the documentation for which you are currently reading. This is the core component of the library.
///
/// 2. PRESENTATION -- the optional DSDL support extension library. This is implemented in canard_dsdl.c/.h,
/// an optional component which may be used by some applications where automatic DSDL code generation is
/// not used. Normally, applications may prefer to rely on auto-generated code using DSDL-to-C translators
/// such as Nunavut (https://github.com/UAVCAN/nunavut).
///
/// The DSDL extension is trivial and there is not much to document -- please refer to its header file for details.
///
/// This transport layer implementation consists of two components: the transmission (TX) pipeline and the
/// reception (RX) pipeline.
///
/// The TX and RX pipelines are completely independent from each other except that they both rely on the same
/// dynamic memory manager. The TX pipeline uses the dynamic memory to store outgoing CAN frames in the prioritized
/// transmission queue. The RX pipeline uses the dynamic memory to store contiguous payload buffers for received
/// transfers and for keeping the transfer reassembly state machine data. The exact memory consumption model is defined
/// for both pipelines, so it is possible to statically determine the minimum size of the dynamic memory pool required
/// to guarantee that a given application will never encounter an out-of-memory error at runtime.
///
/// Much like with dynamic memory, the time complexity of every API function is well-characterized, allowing the
/// application to guarantee predictable real-time performance.
///
/// The TX pipeline is managed with the help of three API functions. When the application needs to emit a transfer,
/// it invokes canardTxPush(). The function splits the transfer into CAN frames and stores them into the prioritized
/// transmission queue. The application then picks the CAN frames from the queue one-by-one by calling canardTxPeek()
/// followed by canardTxPop() -- the former allows the application to look at the frame and the latter tells the library
/// that the frame shall be removed from the queue. The returned frames need to be deallocated by the application.
///
/// The RX pipeline is managed with the help of three API functions. The main function canardRxAccept() takes a
/// received CAN frame and updates the appropriate transfer reassembly state machine. The functions canardRxSubscribe()
/// and its counterpart canardRxUnsubscribe() instruct the library which transfers should be received (by default, all
/// transfers are ignored); also the subscription function specifies vital transfer reassembly parameters such as the
/// maximum payload size (i.e., the maximum size of a serialized representation of a DSDL object) and the transfer-ID
/// timeout. Transfers that carry more payload than the configured maximum per subscription are truncated following the
/// Implicit Truncation Rule (ITR) defined by the UAVCAN Specification -- the rule is implemented to facilitate
/// backward-compatible DSDL data type extensibility.
///
/// The library supports a practically unlimited number of redundant transports.
///
/// The library is not thread-safe: if used in a concurrent environment, it is the responsibility of the application
/// to provide adequate synchronization.
///
/// The library is purely reactive: it does not perform any background processing and does not require periodic
/// servicing. Its internal state is only updated as a response to well-specified explicit API calls.
///
/// --------------------------------------------------------------------------------------------------------------------
///
/// This software is distributed under the terms of the MIT License.
/// Copyright (c) 2016-2020 UAVCAN Development Team.
/// Author: Pavel Kirienko <pavel.kirienko@zubax.com>
/// Contributors: https://github.com/UAVCAN/libcanard/contributors.
#ifndef CANARD_H_INCLUDED
#define CANARD_H_INCLUDED
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/// Semantic version of this library (not the UAVCAN specification).
/// API will be backward compatible within the same major version.
#define CANARD_VERSION_MAJOR 1
#define CANARD_VERSION_MINOR 0
/// The version number of the UAVCAN specification implemented by this library.
#define CANARD_UAVCAN_SPECIFICATION_VERSION_MAJOR 1
#define CANARD_UAVCAN_SPECIFICATION_VERSION_MINOR 0
/// These error codes may be returned from the library API calls whose return type is a signed integer in the negated
/// form (e.g., error code 2 returned as -2). A non-negative return value represents success.
/// API calls whose return type is not a signed integer cannot fail by contract.
/// No other error states may occur in the library.
/// By contract, a well-characterized application with a properly sized memory pool will never encounter errors.
/// The error code 1 is not used because -1 is often used as a generic error code in 3rd-party code.
#define CANARD_ERROR_INVALID_ARGUMENT 2
#define CANARD_ERROR_OUT_OF_MEMORY 3
/// MTU values for the supported protocols.
/// Per the recommendations given in the UAVCAN/CAN Specification, other MTU values should not be used.
#define CANARD_MTU_CAN_CLASSIC 8U
#define CANARD_MTU_CAN_FD 64U
/// Parameter ranges are inclusive; the lower bound is zero for all. See UAVCAN/CAN Specification for background.
#define CANARD_SUBJECT_ID_MAX 8191U
#define CANARD_SERVICE_ID_MAX 511U
#define CANARD_NODE_ID_MAX 127U
#define CANARD_PRIORITY_MAX 7U
#define CANARD_TRANSFER_ID_BIT_LENGTH 5U
#define CANARD_TRANSFER_ID_MAX ((1U << CANARD_TRANSFER_ID_BIT_LENGTH) - 1U)
/// This value represents an undefined node-ID: broadcast destination or anonymous source.
/// Library functions treat all values above CANARD_NODE_ID_MAX as anonymous.
#define CANARD_NODE_ID_UNSET 255U
/// This is the recommended transfer-ID timeout value given in the UAVCAN Specification. The application may choose
/// different values per subscription (i.e., per data specifier) depending on its timing requirements.
#define CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC 2000000UL
// Forward declarations.
typedef struct CanardInstance CanardInstance;
typedef uint64_t CanardMicrosecond;
typedef uint16_t CanardPortID;
typedef uint8_t CanardNodeID;
typedef uint8_t CanardTransferID;
/// Transfer priority level mnemonics per the recommendations given in the UAVCAN Specification.
typedef enum
{
CanardPriorityExceptional = 0,
CanardPriorityImmediate = 1,
CanardPriorityFast = 2,
CanardPriorityHigh = 3,
CanardPriorityNominal = 4, ///< Nominal priority level should be the default.
CanardPriorityLow = 5,
CanardPrioritySlow = 6,
CanardPriorityOptional = 7,
} CanardPriority;
/// Transfer kinds as defined by the UAVCAN Specification.
typedef enum
{
CanardTransferKindMessage = 0, ///< Multicast, from publisher to all subscribers.
CanardTransferKindResponse = 1, ///< Point-to-point, from server to client.
CanardTransferKindRequest = 2, ///< Point-to-point, from client to server.
} CanardTransferKind;
#define CANARD_NUM_TRANSFER_KINDS 3
/// CAN data frame with an extended 29-bit ID. RTR/Error frames are not used and therefore not modeled here.
/// CAN frames with 11-bit ID are not used by UAVCAN/CAN and so they are not supported by the library.
typedef struct
{
/// For RX frames: reception timestamp.
/// For TX frames: transmission deadline.
/// The time system may be arbitrary as long as the clock is monotonic (steady).
CanardMicrosecond timestamp_usec;
/// 29-bit extended ID. The bits above 29-th shall be zero.
uint32_t extended_can_id;
/// The useful data in the frame. The length value is not to be confused with DLC!
/// If the payload is empty (payload_size = 0), the payload pointer may be NULL.
/// For RX frames: the library does not expect the lifetime of the pointee to extend beyond the point of return
/// from the API function. That is, the pointee can be invalidated immediately after the frame has been processed.
/// For TX frames: the frame and the payload are allocated within the same dynamic memory fragment, so their
/// lifetimes are identical; when the frame is freed, the payload is invalidated.
/// A more detailed overview of the dataflow and related resource management issues is provided in the API docs.
size_t payload_size;
const void* payload;
} CanardFrame;
/// Conversion look-up table from CAN DLC to data length.
extern const uint8_t CanardCANDLCToLength[16];
/// Conversion look-up table from data length to CAN DLC; the length is rounded up.
extern const uint8_t CanardCANLengthToDLC[65];
/// A UAVCAN transfer model (either incoming or outgoing).
/// Per Specification, a transfer is represented on the wire as a non-empty set of transport frames (i.e., CAN frames).
/// The library is responsible for serializing transfers into transport frames when transmitting, and reassembling
/// transfers from an incoming stream of frames during reception.
typedef struct
{
/// For RX transfers: reception timestamp.
/// For TX transfers: transmission deadline.
/// The time system may be arbitrary as long as the clock is monotonic (steady).
CanardMicrosecond timestamp_usec;
/// Per the Specification, all frames belonging to a given transfer shall share the same priority level.
/// If this is not the case, then this field contains the priority level of the last frame to arrive.
CanardPriority priority;
CanardTransferKind transfer_kind;
/// Subject-ID for message publications; service-ID for service requests/responses.
CanardPortID port_id;
/// For outgoing message transfers the value shall be CANARD_NODE_ID_UNSET (otherwise the state is invalid).
/// For outgoing service transfers this is the destination address (invalid if unset).
/// For incoming non-anonymous transfers this is the node-ID of the origin.
/// For incoming anonymous transfers the value is reported as CANARD_NODE_ID_UNSET.
CanardNodeID remote_node_id;
/// When responding to a service request, the response transfer SHALL have the same transfer-ID value as the
/// request because the client will match the response with the request based on that.
///
/// When publishing a message transfer, the value SHALL be one greater than the previous transfer under the same
/// subject-ID; the initial value should be zero.
///
/// When publishing a service request transfer, the value SHALL be one greater than the previous transfer under
/// the same service-ID addressed to the same server node-ID; the initial value should be zero.
///
/// Upon overflow, the value SHALL be reset back to zero.
/// Values above CANARD_TRANSFER_ID_MAX are permitted -- the library will compute the modulo automatically.
/// For received transfers, the values never exceed CANARD_TRANSFER_ID_MAX.
///
/// A simple and robust way of managing transfer-ID counters is to keep a separate static variable per subject-ID
/// and per (service-ID, server-node-ID) pair.
CanardTransferID transfer_id;
/// This is the actual transfer payload.
/// If the payload is empty (payload_size = 0), the payload pointer may be NULL.
/// The const pointer makes it incompatible with memory deallocation function, this is due to the limitations of C;
/// therefore, when freeing the memory allocated for the payload, cast away the pointer's const qualifier.
/// For RX transfers: the application is required to free the payload buffer after the transfer is processed.
/// For TX transfers: the library does not expect the lifetime of the payload buffer to extend beyond the point
/// of return from the API function because the payload is copied into the TX frame objects.
/// A more detailed overview of the dataflow and related resource management issues is provided in the API docs.
size_t payload_size;
const void* payload;
} CanardTransfer;
/// Transfer subscription state. The application can register its interest in a particular kind of data exchanged
/// over the bus by creating such subscription objects. Frames that carry data for which there is no active
/// subscription will be silently dropped by the library.
///
/// WARNING: SUBSCRIPTION INSTANCES SHALL NOT BE COPIED OR MUTATED BY THE APPLICATION.
///
/// Every field is named starting with an underscore to emphasize that the application shall not modify it.
/// Unfortunately, C, being such a limited language, does not allow us to construct a better API.
///
/// The memory footprint of a subscription is large. On a 32-bit platform it slightly exceeds half a KiB.
/// This is an intentional time-memory trade-off: use a large look-up table to ensure predictable temporal properties.
typedef struct CanardRxSubscription
{
struct CanardRxSubscription* _next; ///< Internal use only.
/// The current architecture is an acceptable middle ground between worst-case execution time and memory
/// consumption. Instead of statically pre-allocating a dedicated RX session for each remote node-ID here in
/// this table, we only keep pointers, which are NULL by default, populating a new RX session dynamically
/// on an ad-hoc basis when we first receive a transfer from that node. This is deterministic because our memory
/// allocation routines are assumed to be deterministic and we make at most one allocation per remote node,
/// but the disadvantage is that these additional operations lift the upper bound on the execution time.
/// Further, the pointers here add an extra indirection, which is bad for systems that leverage cached memory,
/// plus a pointer itself takes about 2-8 bytes of memory, too.
///
/// A far more predictable and a much simpler approach is to pre-allocate states here statically instead of keeping
/// just pointers, but it would push the size of this instance from about 0.5 KiB to ~3 KiB for a typical 32-bit
/// system. Since this is a general-purpose library, we have to pick a middle ground so we use the more complex
/// but more memory-efficient approach.
struct CanardInternalRxSession* _sessions[CANARD_NODE_ID_MAX + 1U];
CanardMicrosecond _transfer_id_timeout_usec; ///< Internal use only.
size_t _extent; ///< Internal use only.
CanardPortID _port_id; ///< Internal use only.
} CanardRxSubscription;
/// A pointer to the memory allocation function. The semantics are similar to malloc():
/// - The returned pointer shall point to an uninitialized block of memory that is at least "amount" bytes large.
/// - If there is not enough memory, the returned pointer shall be NULL.
/// - The memory shall be aligned at least at max_align_t.
/// - The execution time should be constant (O(1)).
/// - The worst-case memory fragmentation should be bounded and easily predictable.
/// If the standard dynamic memory manager of the target platform does not satisfy the above requirements,
/// consider using O1Heap: https://github.com/pavel-kirienko/o1heap.
typedef void* (*CanardMemoryAllocate)(CanardInstance* ins, size_t amount);
/// The counterpart of the above -- this function is invoked to return previously allocated memory to the allocator.
/// The semantics are similar to free():
/// - The pointer was previously returned by the allocation function.
/// - The pointer may be NULL, in which case the function shall have no effect.
/// - The execution time should be constant (O(1)).
typedef void (*CanardMemoryFree)(CanardInstance* ins, void* pointer);
/// This is the core structure that keeps all of the states and allocated resources of the library instance.
/// The application may directly alter the fields whose names do not begin with an underscore.
struct CanardInstance
{
/// User pointer that can link this instance with other objects.
/// This field can be changed arbitrarily, the library does not access it after initialization.
/// The default value is NULL.
void* user_reference;
/// The transport-layer maximum transmission unit (MTU). The value can be changed arbitrarily at any time.
/// This setting defines the maximum number of bytes per CAN data frame in all outgoing transfers.
/// Regardless of this setting, CAN frames with any MTU can always be accepted.
///
/// Only the standard values should be used as recommended by the specification;
/// otherwise, networking interoperability issues may arise. See recommended values CANARD_MTU_*.
///
/// Valid values are any valid CAN frame data length value not smaller than 8.
/// Invalid values are treated as the nearest valid value. The default is the maximum valid value.
size_t mtu_bytes;
/// The node-ID of the local node.
/// Per the UAVCAN Specification, the node-ID should not be assigned more than once.
/// Invalid values are treated as CANARD_NODE_ID_UNSET. The default value is CANARD_NODE_ID_UNSET.
CanardNodeID node_id;
/// Dynamic memory management callbacks. See their type documentation for details.
/// They SHALL be valid function pointers at all times.
/// The time complexity models given in the API documentation are made on the assumption that the memory management
/// functions have constant complexity O(1).
///
/// The following API functions may allocate memory: canardRxAccept(), canardTxPush().
/// The following API functions may deallocate memory: canardRxAccept(), canardRxSubscribe(), canardRxUnsubscribe().
/// The exact memory requirement and usage model is specified for each function in its documentation.
CanardMemoryAllocate memory_allocate;
CanardMemoryFree memory_free;
/// These fields are for internal use only. Do not access from the application.
CanardRxSubscription* _rx_subscriptions[CANARD_NUM_TRANSFER_KINDS];
struct CanardInternalTxQueueItem* _tx_queue;
};
/// Construct a new library instance.
/// The default values will be assigned as specified in the structure field documentation.
/// If any of the pointers are NULL, the behavior is undefined.
///
/// The instance does not hold any resources itself except for the allocated memory.
/// If the instance should be de-initialized, the application shall clear the TX queue by calling the pop function
/// repeatedly, and remove all RX subscriptions. Once that is done, the instance will be holding no memory resources,
/// so it can be discarded freely.
///
/// The time complexity is constant. This function does not invoke the dynamic memory manager.
CanardInstance canardInit(const CanardMemoryAllocate memory_allocate, const CanardMemoryFree memory_free);
/// This function serializes a transfer into a sequence of transport frames and inserts them into the prioritized
/// transmission queue at the appropriate position. Afterwards, the application is supposed to take the enqueued frames
/// from the transmission queue using the function canardTxPeek() and transmit them. Each transmitted (or otherwise
/// discarded, e.g., due to timeout) frame should be removed from the queue using canardTxPop(). The queue is
/// prioritized following the normal CAN frame arbitration rules to avoid the inner priority inversion. The transfer
/// payload will be copied into the transmission queue so that the lifetime of the frames is not related to the
/// lifetime of the input transfer instance or its payload buffer.
///
/// The MTU of the generated frames is dependent on the value of the MTU setting at the time when this function
/// is invoked. The MTU setting can be changed arbitrarily between invocations. No other functions rely on that
/// parameter.
///
/// The timestamp value of the transfer will be used to populate the timestamp values of the resulting transport
/// frames (so all frames will have the same timestamp value). This feature is intended to facilitate transmission
/// deadline tracking, i.e., aborting frames that could not be transmitted before the specified deadline.
/// Therefore, normally, the timestamp value should be in the future.
/// The library itself, however, does not use or check this value in any way, so it can be zero if not needed.
///
/// The function returns the number of frames enqueued into the prioritized TX queue (which is always a positive
/// number) in case of success (so that the application can track the number of items in the TX queue if necessary).
/// In case of failure, the function returns a negated error code: either invalid argument or out-of-memory.
///
/// An invalid argument error may be returned in the following cases:
/// - Any of the input arguments are NULL.
/// - The remote node-ID is not CANARD_NODE_ID_UNSET and the transfer is a message transfer.
/// - The remote node-ID is above CANARD_NODE_ID_MAX and the transfer is a service transfer.
/// - The priority, subject-ID, or service-ID exceed their respective maximums.
/// - The transfer kind is invalid.
/// - The payload pointer is NULL while the payload size is nonzero.
/// - The local node is anonymous and a message transfer is requested that requires a multi-frame transfer.
/// - The local node is anonymous and a service transfer is requested.
/// The following cases are handled without raising an invalid argument error:
/// - If the transfer-ID is above the maximum, the excessive bits are silently masked away
/// (i.e., the modulo is computed automatically, so the caller doesn't have to bother).
///
/// An out-of-memory error is returned if a TX frame could not be allocated due to the memory being exhausted.
/// In that case, all previously allocated frames will be deallocated automatically. In other words, either all frames
/// of the transfer are enqueued successfully, or none are.
///
/// The time complexity is O(p+e), where p is the amount of payload in the transfer, and e is the number of frames
/// already enqueued in the transmission queue.
///
/// The memory allocation requirement is one allocation per transport frame. A single-frame transfer takes one
/// allocation; a multi-frame transfer of N frames takes N allocations. The maximum size of each allocation is
/// (sizeof(CanardFrame) + sizeof(void*) + MTU).
int32_t canardTxPush(CanardInstance* const ins, const CanardTransfer* const transfer);
/// This function accesses the top element of the prioritized transmission queue. The queue itself is not modified
/// (i.e., the accessed element is not removed). The application should invoke this function to collect the transport
/// frames of serialized transfers pushed into the prioritized transmission queue by canardTxPush().
///
/// Nodes with redundant transports should replicate every frame into each of the transport interfaces.
/// Such replication may require additional buffering in the media I/O layer, depending on the implementation.
///
/// The timestamp values of returned frames are initialized with the timestamp value of the transfer instance they
/// originate from. Timestamps are used to specify the transmission deadline. It is up to the application and/or
/// the media layer to implement the discardment of timed-out transport frames. The library does not check it,
/// so a frame that is already timed out may be returned here.
///
/// If the queue is empty or if the argument is NULL, the returned value is NULL.
///
/// If the queue is non-empty, the returned value is a pointer to its top element (i.e., the next frame to transmit).
/// The returned pointer points to an object allocated in the dynamic storage; it should be eventually freed by the
/// application by calling CanardInstance::memory_free(). The memory shall not be freed before the entry is removed
/// from the queue by calling canardTxPop(); this is because until canardTxPop() is executed, the library retains
/// ownership of the object. The pointer retains validity until explicitly freed by the application; in other words,
/// calling canardTxPop() does not invalidate the object.
///
/// The payload buffer is located shortly after the object itself, in the same memory fragment. The application shall
/// not attempt to free it.
///
/// The time complexity is constant. This function does not invoke the dynamic memory manager.
const CanardFrame* canardTxPeek(const CanardInstance* const ins);
/// This function transfers the ownership of the top element of the prioritized transmission queue to the application.
/// The application should invoke this function to remove the top element from the prioritized transmission queue.
/// The element is removed but it is not invalidated; it is the responsibility of the application to deallocate
/// the memory used by the object later. The object SHALL NOT be deallocated UNTIL this function is invoked.
///
/// WARNING:
/// Invocation of canardTxPush() may add new elements at the top of the prioritized transmission queue.
/// The calling code shall take that into account to eliminate the possibility of data loss and memory leak due to
/// the frame at the top of the queue being unexpectedly replaced between calls of canardTxPeek() and this function.
///
/// If the input argument is NULL or if the transmission queue is empty, the function has no effect.
///
/// The time complexity is constant. This function does not invoke the dynamic memory manager.
void canardTxPop(CanardInstance* const ins);
/// This function implements the transfer reassembly logic. It accepts a transport frame, locates the appropriate
/// subscription state, and, if found, updates it. If the frame completed a transfer, the return value is 1 (one)
/// and the out_transfer pointer is populated with the parameters of the newly reassembled transfer. The transfer
/// reassembly logic is defined in the UAVCAN specification.
///
/// The MTU of the accepted frame is not limited and is not dependent on the MTU setting of the local node;
/// that is, any MTU is accepted. The DLC compliance is also not checked.
///
/// Any value of redundant_transport_index is accepted; that is, up to 256 redundant transports are supported.
/// The index of the transport from which the transfer is accepted is always the same as redundant_transport_index
/// of the current invocation, so the application can always determine which transport has delivered the transfer.
///
/// The function invokes the dynamic memory manager in the following cases only:
///
/// 1. New memory for a session state object is allocated when a new session is initiated.
/// This event occurs when a transport frame that matches a known subscription is received from a node that
/// did not emit matching frames since the subscription was created.
/// Once a new session is created, it is not destroyed until the subscription is terminated by invoking
/// canardRxUnsubscribe(). The number of sessions is bounded and the bound is low (at most the number of nodes
/// in the network minus one), also the size of a session instance is very small, so the removal is unnecessary.
/// Real-time networks typically do not change their configuration at runtime, so it is possible to reduce
/// the time complexity by never deallocating sessions.
/// The size of a session instance is at most 48 bytes on any conventional platform (typically much smaller).
///
/// 2. New memory for the transfer payload buffer is allocated when a new transfer is initiated, unless the buffer
/// was already allocated at the time.
/// This event occurs when a transport frame that matches a known subscription is received and it begins a
/// new transfer (that is, the start-of-frame flag is set and it is not a duplicate).
/// The amount of the allocated memory equals the extent as configured via canardRxSubscribe(); please read
/// its documentation for further information about the extent and related edge cases.
/// The worst case occurs when every node on the bus initiates a multi-frame transfer for which there is a
/// matching subscription: in this case, the library will allocate number_of_nodes allocations, where each
/// allocation is the same size as the configured extent.
///
/// 3. Memory allocated for the transfer payload buffer may be deallocated at the discretion of the library.
/// This operation does not increase the worst case execution time and does not improve the worst case memory
/// consumption, so a deterministic application need not consider this behavior in the resource analysis.
/// This behavior is implemented for the benefit of applications where rigorous characterization is unnecessary.
///
/// The worst case dynamic memory consumption per subscription is:
///
/// (sizeof(session instance) + extent) * number_of_nodes
///
/// Where sizeof(session instance) and extent are defined above, and number_of_nodes is the number of remote
/// nodes emitting transfers that match the subscription (which cannot exceed (CANARD_NODE_ID_MAX-1) by design).
/// If the dynamic memory pool is sized correctly, the application is guaranteed to never encounter an
/// out-of-memory (OOM) error at runtime. The actual size of the dynamic memory pool is typically larger;
/// for a detailed treatment of the problem and the related theory please refer to the documentation of O1Heap --
/// a deterministic memory allocator for hard real-time embedded systems.
///
/// The time complexity is O(n+p) where n is the number of subject-IDs or service-IDs subscribed to by the application,
/// depending on the transfer kind of the supplied frame, and p is the amount of payload in the received frame
/// (because it will be copied into an internal contiguous buffer). Observe that the time complexity is invariant to
/// the network configuration (such as the number of online nodes) -- this is a very important design guarantee for
/// real-time applications because the execution time is dependent only on the number of active subscriptions for
/// a given transfer kind, and the MTU, both of which are easy to predict and account for. Excepting the
/// subscription search and the payload data copying, the entire RX pipeline contains neither loops nor recursion.
/// Misaddressed and malformed frames are discarded in constant time.
///
/// The function returns 1 (one) if the new frame completed a transfer. In this case, the details of the transfer
/// are stored into out_transfer, and the transfer payload buffer ownership is passed to that object. The lifetime
/// of the resulting transfer object is not related to the lifetime of the input transport frame (that is, even if
/// it is a single-frame transfer, its payload is copied out into a new dynamically allocated buffer storage).
/// If the extent is zero, the payload pointer may be NULL, since there is no data to store and so a
/// buffer is not needed. The application is responsible for deallocating the payload buffer when the processing
/// is done by invoking memory_free on the transfer payload pointer.
///
/// The function returns a negated out-of-memory error if it was unable to allocate dynamic memory.
///
/// The function does nothing and returns a negated invalid argument error immediately if any condition is true:
/// - Any of the input arguments that are pointers are NULL.
/// - The payload pointer of the input frame is NULL while its size is non-zero.
/// - The CAN ID of the input frame is not less than 2**29=0x20000000.
///
/// The function returns zero if any of the following conditions are true (the general policy is that protocol
/// errors are not escalated because they do not construe a node-local error):
/// - The received frame is not a valid UAVCAN/CAN transport frame.
/// - The received frame is a valid UAVCAN/CAN transport frame, but there is no matching subscription,
/// the frame did not complete a transfer, the frame forms an invalid frame sequence, the frame is a duplicate,
/// the frame is unicast to a different node (address mismatch).
///
/// The function is designed to facilitate almost zero-copy data exchange across the protocol stack: once a buffer is
/// allocated, its data is never copied around but only passed by reference. This design allows us to reduce the
/// worst-case execution time and reduce the jitter caused by the linear time complexity of memcpy().
/// One data copy still has to take place, though: from the frame payload into the contiguous transfer payload buffer.
/// In CAN, the MTU is small (at most 64 bytes for CAN FD), so the extra copy does not cost us much here,
/// but it allows us to completely decouple the lifetime of the input frame buffer from the lifetime of the final
/// transfer object, regardless of whether it's a single-frame or a multi-frame transfer.
/// If we were building, say, an UAVCAN/UDP library, then we would likely resort to a different design, where the
/// frame buffer is allocated once from the heap (which may be done from the interrupt handler if the heap is
/// sufficiently deterministic), and in the case of single-frame transfer it is then carried over to the application
/// without copying. This design somewhat complicates the media layer though.
int8_t canardRxAccept(CanardInstance* const ins,
const CanardFrame* const frame,
const uint8_t redundant_transport_index,
CanardTransfer* const out_transfer);
/// This function creates a new subscription, allowing the application to register its interest in a particular
/// category of transfers. The library will reject all transport frames for which there is no active subscription.
/// The reference out_subscription shall retain validity until the subscription is terminated (the referred object
/// cannot be moved or destroyed).
///
/// If such subscription already exists, it will be removed first as if canardRxUnsubscribe() was
/// invoked by the application, and then re-created anew with the new parameters.
///
/// The extent defines the size of the transfer payload memory buffer; or, in other words, the maximum possible size
/// of received objects, considering also possible future versions with new fields. It is safe to pick larger values.
/// Note well that the extent is not the same thing as the maximum size of the object, it is usually larger!
/// Transfers that carry payloads that exceed the specified extent will be accepted anyway but the excess payload
/// will be truncated away, as mandated by the Specification. The transfer CRC is always validated regardless of
/// whether its payload is truncated.
///
/// The default transfer-ID timeout value is defined as CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC; use it if not sure.
/// The redundant transport fail-over timeout (if redundant transports are used) is the same as the transfer-ID timeout.
/// It may be reduced in a future release of the library, but it will not affect the backward compatibility.
///
/// The return value is 1 if a new subscription has been created as requested.
/// The return value is 0 if such subscription existed at the time the function was invoked. In this case,
/// the existing subscription is terminated and then a new one is created in its place. Pending transfers may be lost.
/// The return value is a negated invalid argument error if any of the input arguments are invalid.
///
/// The time complexity is linear from the number of current subscriptions under the specified transfer kind.
/// This function does not allocate new memory. The function may deallocate memory if such subscription already
/// existed; the deallocation behavior is specified in the documentation for canardRxUnsubscribe().
///
/// Subscription instances have large look-up tables to ensure that the temporal properties of the algorithms are
/// invariant to the network configuration (i.e., a node that is validated on a network containing one other node
/// will provably perform identically on a network that contains X nodes).
/// This is a conscious time-memory trade-off. It may have adverse effects on RAM-constrained applications,
/// but this is considered tolerable because it is expected that the types of applications leveraging Libcanard
/// will be either real-time function nodes where time determinism is critical, or bootloaders where time determinism
/// is usually not required but the amount of available memory is not an issue (the main constraint is ROM, not RAM).
int8_t canardRxSubscribe(CanardInstance* const ins,
const CanardTransferKind transfer_kind,
const CanardPortID port_id,
const size_t extent,
const CanardMicrosecond transfer_id_timeout_usec,
CanardRxSubscription* const out_subscription);
/// This function reverses the effect of canardRxSubscribe().
/// If the subscription is found, all its memory is de-allocated (session states and payload buffers); to determine
/// the amount of memory freed, please refer to the memory allocation requirement model of canardRxAccept().
///
/// The return value is 1 if such subscription existed (and, therefore, it was removed).
/// The return value is 0 if such subscription does not exist. In this case, the function has no effect.
/// The return value is a negated invalid argument error if any of the input arguments are invalid.
///
/// The time complexity is linear from the number of current subscriptions under the specified transfer kind.
/// This function does not allocate new memory.
int8_t canardRxUnsubscribe(CanardInstance* const ins,
const CanardTransferKind transfer_kind,
const CanardPortID port_id);
#ifdef __cplusplus
}
#endif
#endif