Files
xiuos/Ubiquitous/XiZi_IIoT/tool/libcanard/canard.h
T
2024-12-11 18:56:08 +08:00

715 lines
45 KiB
C

/// ____ ______ __ __
/// / __ `____ ___ ____ / ____/_ ______ / /_ ____ / /
/// / / / / __ `/ _ `/ __ `/ / / / / / __ `/ __ `/ __ `/ /
/// / /_/ / /_/ / __/ / / / /___/ /_/ / /_/ / / / / /_/ / /
/// `____/ .___/`___/_/ /_/`____/`__, / .___/_/ /_/`__,_/_/
/// /_/ /____/_/
///
/// Libcanard is a compact implementation of the Cyphal/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 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 its source files into the
/// source tree of the project; it does not require any special compilation options and should work out of the box.
/// There are build-time configuration parameters defined near the top of canard.c, but they are safe to ignore.
///
/// 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 are no specific requirements to the underlying I/O layer. Some low-level drivers maintained by the
/// OpenCyphal team may be found at https://github.com/OpenCyphal-Garage/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.opencyphal.org.
///
/// ARCHITECTURE
///
/// Cyphal, 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 Cyphal/CAN. This library
/// is focused on Cyphal/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; these parts are out of the scope of
/// this library as it is focused purely on the transport.
///
/// This library consists of two components: the transmission (TX) pipeline and the reception (RX) pipeline.
/// The 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 four API functions. The first one -- canardTxInit() -- is used for
/// constructing a new TX queue, of which there should be as many as there are redundant CAN interfaces;
/// each queue is managed independently. When the application needs to emit a transfer, it invokes canardTxPush()
/// on each queue separately. The function splits the transfer into CAN frames and stores them into the queue.
/// The application then picks the produced CAN frames from the queue one-by-one by calling canardTxPeek() followed
/// by canardTxPop() -- the former allows the application to look at the next frame scheduled for transmission,
/// and the latter tells the library that the frame shall be removed from the queue.
/// Popped frames need to be manually deallocated by the application upon transmission.
///
/// The RX pipeline is managed with the help of three API functions; unlike the TX pipeline, there is one shared
/// state for all redundant interfaces that manages deduplication transparently. 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 Cyphal Specification --
/// the rule is implemented to facilitate backward-compatible DSDL data type extensibility.
///
/// The library supports a practically unlimited number of redundant interfaces.
///
/// 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 external events.
///
/// --------------------------------------------------------------------------------------------------------------------
///
/// This software is distributed under the terms of the MIT License.
/// Copyright (c) 2016 OpenCyphal.
/// Author: Pavel Kirienko <pavel@opencyphal.org>
/// Contributors: https://github.com/OpenCyphal/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 Cyphal specification).
/// API will be backward compatible within the same major version.
#define CANARD_VERSION_MAJOR 3
#define CANARD_VERSION_MINOR 3
/// The version number of the Cyphal specification implemented by this library.
#define CANARD_CYPHAL_SPECIFICATION_VERSION_MAJOR 1
#define CANARD_CYPHAL_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 Cyphal/CAN Specification, other MTU values should not be used.
#define CANARD_MTU_CAN_CLASSIC 8U
#define CANARD_MTU_CAN_FD 64U
#define CANARD_MTU_MAX CANARD_MTU_CAN_FD
/// Parameter ranges are inclusive; the lower bound is zero for all. See Cyphal/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 Cyphal 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 struct CanardTreeNode CanardTreeNode;
typedef struct CanardTxQueueItem CanardTxQueueItem;
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 Cyphal 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 Cyphal 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
/// The AVL tree node structure is exposed here to avoid pointer casting/arithmetics inside the library.
/// The user code is not expected to interact with this type except if advanced introspection is required.
struct CanardTreeNode
{
CanardTreeNode* up; ///< Do not access this field.
CanardTreeNode* lr[2]; ///< Left and right children of this node may be accessed for tree traversal.
int8_t bf; ///< Do not access this field.
};
/// 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 Cyphal/CAN and so they are not supported by the library.
typedef struct
{
/// 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 Cyphal transfer metadata (everything except the payload).
/// 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 (possibly duplicated if redundant interfaces are used) during reception.
typedef struct
{
/// 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;
} CanardTransferMetadata;
/// Prioritized transmission queue that keeps CAN frames destined for transmission via one CAN interface.
/// Applications with redundant interfaces are expected to have one instance of this type per interface.
/// Applications that are not interested in transmission may have zero queues.
/// All operations (push, peek, pop) are O(log n); there is exactly one heap allocation per element.
/// API functions that work with this type are named "canardTx*()", find them below.
typedef struct CanardTxQueue
{
/// The maximum number of frames this queue is allowed to contain. An attempt to push more will fail with an
/// out-of-memory error even if the memory is not exhausted. This value can be changed by the user at any moment.
/// The purpose of this limitation is to ensure that a blocked queue does not exhaust the heap memory.
size_t capacity;
/// The transport-layer maximum transmission unit (MTU). The value can be changed arbitrarily at any time between
/// pushes. It defines the maximum number of data bytes per CAN data frame in outgoing transfers via this queue.
///
/// 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 number of frames that are currently contained in the queue, initially zero.
/// Do not modify this field!
size_t size;
/// The root of the priority queue is NULL if the queue is empty. Do not modify this field!
CanardTreeNode* root;
/// This field can be arbitrarily mutated by the user. It is never accessed by the library.
/// Its purpose is to simplify integration with OOP interfaces.
void* user_reference;
} CanardTxQueue;
/// One frame stored in the transmission queue along with its metadata.
struct CanardTxQueueItem
{
/// Internal use only; do not access this field.
CanardTreeNode base;
/// Points to the next frame in this transfer or NULL. This field is mostly intended for own needs of the library.
/// Normally, the application would not use it because transfer frame ordering is orthogonal to global TX ordering.
/// It can be useful though for pulling pending frames from the TX queue if at least one frame of their transfer
/// failed to transmit; the idea is that if at least one frame is missing, the transfer will not be received by
/// remote nodes anyway, so all its remaining frames can be dropped from the queue at once using canardTxPop().
CanardTxQueueItem* next_in_transfer;
/// This is the same value that is passed to canardTxPush().
/// Frames whose transmission deadline is in the past shall be dropped.
CanardMicrosecond tx_deadline_usec;
/// The actual CAN frame data.
CanardFrame frame;
};
/// 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. The entire RX pipeline is invariant to the number of
/// redundant CAN interfaces used.
///
/// SUBSCRIPTION INSTANCES SHALL NOT BE MOVED WHILE IN USE.
///
/// 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
{
CanardTreeNode base; ///< Read-only DO NOT MODIFY THIS
CanardMicrosecond transfer_id_timeout_usec;
size_t extent; ///< Read-only DO NOT MODIFY THIS
CanardPortID port_id; ///< Read-only DO NOT MODIFY THIS
/// This field can be arbitrarily mutated by the user. It is never accessed by the library.
/// Its purpose is to simplify integration with OOP interfaces.
void* user_reference;
/// 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 O(1) because our memory
/// allocation routines are assumed to be O(1) and we make at most one allocation per remote node.
///
/// A more predictable and 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]; ///< Read-only DO NOT MODIFY THIS
} CanardRxSubscription;
/// Reassembled incoming transfer returned by canardRxAccept().
typedef struct CanardRxTransfer
{
CanardTransferMetadata metadata;
/// The timestamp of the first received CAN frame of this transfer.
/// The time system may be arbitrary as long as the clock is monotonic (steady).
CanardMicrosecond timestamp_usec;
/// If the payload is empty (payload_size = 0), the payload pointer may be NULL.
/// The application is required to deallocate the payload buffer after the transfer is processed.
size_t payload_size;
void* payload;
} CanardRxTransfer;
/// 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.
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 node-ID of the local node.
/// Per the Cyphal 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;
/// Read-only DO NOT MODIFY THIS
CanardTreeNode* rx_subscriptions[CANARD_NUM_TRANSFER_KINDS];
};
/// CAN acceptance filter configuration with an extended 29-bit ID utilizing an ID + mask filter scheme.
/// Filter configuration can be programmed into a CAN controller to filter out irrelevant messages in hardware.
/// This allows the software application to reduce CPU load spent on processing irrelevant messages.
typedef struct CanardFilter
{
/// 29-bit extended ID. Defines the extended CAN ID to filter incoming frames against.
/// The bits above 29-th shall be zero.
uint32_t extended_can_id;
/// 29-bit extended mask. Defines the bitmask used to enable/disable bits used to filter messages.
/// Only bits that are enabled are compared to the extended_can_id for filtering.
/// The bits above 29-th shall be zero.
uint32_t extended_mask;
} CanardFilter;
/// 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.
/// To safely discard it, simply remove all existing subscriptions, and don't forget about the TX queues.
///
/// The time complexity is constant. This function does not invoke the dynamic memory manager.
CanardInstance canardInit(const CanardMemoryAllocate memory_allocate, const CanardMemoryFree memory_free);
/// Construct a new transmission queue instance with the specified values for capacity and mtu_bytes.
/// No memory allocation is going to take place until the queue is actually pushed to.
/// Applications are expected to have one instance of this type per redundant interface.
///
/// The instance does not hold any resources itself except for the allocated memory.
/// To safely discard it, simply pop all items from the queue.
///
/// The time complexity is constant. This function does not invoke the dynamic memory manager.
CanardTxQueue canardTxInit(const size_t capacity, const size_t mtu_bytes);
/// 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 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.
///
/// The tx_deadline_usec 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,
/// or if the capacity of the queue would be exhausted by this operation. In such cases, all frames allocated for
/// this transfer (if any) 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 + log 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 size of each allocation is
/// (sizeof(CanardTxQueueItem) + MTU).
int32_t canardTxPush(CanardTxQueue* const que,
CanardInstance* const ins,
const CanardMicrosecond tx_deadline_usec,
const CanardTransferMetadata* const metadata,
const size_t payload_size,
const void* const payload);
/// 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().
///
/// The timestamp values of returned frames are initialized with tx_deadline_usec from canardTxPush().
/// 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 logarithmic of the queue size. This function does not invoke the dynamic memory manager.
const CanardTxQueueItem* canardTxPeek(const CanardTxQueue* const que);
/// This function transfers the ownership of the specified element of the prioritized transmission queue from the queue
/// to the application. The element does not necessarily need to be the top one -- it is safe to dequeue any element.
/// The element is dequeued but not invalidated; it is the responsibility of the application to deallocate the
/// memory used by the object later. The memory SHALL NOT be deallocated UNTIL this function is invoked.
/// The function returns the same pointer that it is given except that it becomes mutable.
///
/// If any of the arguments are NULL, the function has no effect and returns NULL.
///
/// The time complexity is logarithmic of the queue size. This function does not invoke the dynamic memory manager.
CanardTxQueueItem* canardTxPop(CanardTxQueue* const que, const CanardTxQueueItem* const item);
/// This function implements the transfer reassembly logic. It accepts a transport frame from any of the redundant
/// interfaces, 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 Cyphal specification.
///
/// The MTU of the accepted frame can be arbitrary; that is, any MTU is accepted. The DLC validity is irrelevant.
///
/// Any value of redundant_iface_index is accepted; that is, up to 256 redundant interfaces are supported.
/// The index of the interface from which the transfer is accepted is always the same as redundant_iface_index
/// of the current invocation, so the application can always determine which interface has delivered the transfer.
///
/// Upon return, the out_subscription pointer will point to the instance of CanardRxSubscription that accepted this
/// frame; if no matching subscription exists (i.e., frame discarded), the pointer will be NULL.
/// If this information is not relevant, set out_subscription to NULL.
/// The purpose of this argument is to allow integration with OOP adapters built on top of libcanard; see also the
/// user_reference provided in CanardRxSubscription.
///
/// 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(p + log n) 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 Cyphal/CAN transport frame.
/// - The received frame is a valid Cyphal/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).
int8_t canardRxAccept(CanardInstance* const ins,
const CanardMicrosecond timestamp_usec,
const CanardFrame* const frame,
const uint8_t redundant_iface_index,
CanardRxTransfer* const out_transfer,
CanardRxSubscription** const out_subscription);
/// 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 interface fail-over timeout (if redundant interfaces 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 logarithmic 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.
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 logarithmic 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);
/// This function allows to check the effect of canardRxSubscribe() and canardRxUnsubscribe().
///
/// The return value is 1 if the specified subscription exists, 0 otherwise.
/// The return value is a negated invalid argument error if any of the input arguments are invalid.
/// Output out_subscription could be NULL, but if it is not, it will be populated with the pointer to the existing
/// subscription. In case the subscription does not exist (or error), out_subscription won't be touched.
/// Result pointer to the subscription is valid until the subscription is terminated.
///
/// The time complexity is logarithmic from the number of current subscriptions under the specified transfer kind.
/// This function does not allocate new memory.
int8_t canardRxGetSubscription(CanardInstance* const ins,
const CanardTransferKind transfer_kind,
const CanardPortID port_id,
CanardRxSubscription** const out_subscription);
/// Utilities for generating CAN controller hardware acceptance filter configurations
/// to accept specific subjects, services, or nodes.
///
/// Complex applications will likely subscribe to more subject IDs than there are
/// acceptance filters available in the CAN hardware. In this case, the application
/// should implement filter consolidation. See canardConsolidateFilters()
/// as well as the Cyphal specification for details.
/// Generate an acceptance filter configuration to accept a specific subject ID.
CanardFilter canardMakeFilterForSubject(const CanardPortID subject_id);
/// Generate an acceptance filter configuration to accept both requests and responses for a specific service.
///
/// Users may prefer to instead use a catch-all acceptance filter configuration for accepting
/// all service requests and responses targeted at the specified local node ID.
/// See canardMakeFilterForServices() for this.
CanardFilter canardMakeFilterForService(const CanardPortID service_id, const CanardNodeID local_node_id);
/// Generate an acceptance filter configuration to accept all service
/// requests and responses targeted to the specified local node ID.
///
/// Due to the relatively low frequency of service transfers expected on a network,
/// and the fact that a service directed at a specific node is not likely to be rejected by that node,
/// a user may prefer to use this over canardMakeFilterForService()
/// in order to simplify the API usage and reduce the number of required hardware CAN acceptance filters.
CanardFilter canardMakeFilterForServices(const CanardNodeID local_node_id);
/// Consolidate two acceptance filter configurations into a single configuration.
///
/// Complex applications will likely subscribe to more subject IDs than there are
/// acceptance filters available in the CAN hardware. In this case, the application
/// should implement filter consolidation. While this may make it impossible to create
/// a 'perfect' filter that only accepts desired subject IDs, the application should apply
/// consolidation in a manner that minimizes the number of undesired messages that pass
/// through the hardware acceptance filters and require software filtering (implemented by canardRxSubscribe).
///
/// While optimal choice of filter consolidation is a function of the number of available hardware filters,
/// the set of transfers needed by the application, and the expected frequency of occurrence
/// of all possible distinct transfers on the bus, it is possible to generate a quasi-optimal configuration
/// if information about the frequency of occurrence of different transfers is not known.
/// For details, see the "Automatic hardware acceptance filter configuration" note under the Cyphal/CAN section
/// in the Transport Layer chapter of the Cyphal specification.
CanardFilter canardConsolidateFilters(const CanardFilter* const a, const CanardFilter* const b);
#ifdef __cplusplus
}
#endif
#endif