Skip to content

Commit 5b8226f

Browse files
authored
v4 typedefs and migration guide (#237)
- eliminated struct/enum typedef-s - added migration guide
1 parent 3e70aa5 commit 5b8226f

File tree

7 files changed

+496
-237
lines changed

7 files changed

+496
-237
lines changed

MIGRATION_v3.x_to_v4.0.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Migration Guide: Updating from Libcanard v3.x to v4.0
2+
3+
This guide is intended to help developers migrate their applications from Libcanard version 3.x to version 4.0. It outlines the key changes between the two versions and provides step-by-step instructions to update your code accordingly.
4+
5+
## Introduction
6+
7+
Libcanard is a compact implementation of the Cyphal/CAN protocol designed for high-integrity real-time embedded systems. Version 4 introduces several changes that may impact your existing codebase. This guide will help you understand these changes and update your application accordingly.
8+
9+
These changes do not affect wire compatibility.
10+
11+
## Version Changes
12+
13+
- **Libcanard Version**:
14+
- **Old**: `CANARD_VERSION_MAJOR 3`, `CANARD_VERSION_MINOR 3`
15+
- **New**: `CANARD_VERSION_MAJOR 4`, `CANARD_VERSION_MINOR 0`
16+
- **Cyphal Specification Version**: Remains the same (`1.0`).
17+
18+
## API Changes
19+
20+
### New Functions
21+
22+
- **`canardTxFree`**:
23+
- **Description**: A helper function to free memory allocated for transmission queue items, including the frame payload buffer.
24+
- **Prototype**:
25+
```c
26+
void canardTxFree(
27+
struct CanardTxQueue* const que,
28+
const struct CanardInstance* const ins,
29+
struct CanardTxQueueItem* const item);
30+
```
31+
- **Usage**: After popping a transmission queue item using `canardTxPop`, use `canardTxFree` to deallocate its memory.
32+
33+
### Modified Functions
34+
35+
Several functions have updated prototypes and usage patterns:
36+
37+
1. **`canardInit`**:
38+
- **Old Prototype**:
39+
```c
40+
CanardInstance canardInit(
41+
const CanardMemoryAllocate memory_allocate,
42+
const CanardMemoryFree memory_free);
43+
```
44+
- **New Prototype**:
45+
```c
46+
struct CanardInstance canardInit(
47+
const struct CanardMemoryResource memory);
48+
```
49+
- **Changes**:
50+
- Replaces `CanardMemoryAllocate` and `CanardMemoryFree` function pointers with a `CanardMemoryResource` struct.
51+
52+
2. **`canardTxInit`**:
53+
- **Old Prototype**:
54+
```c
55+
CanardTxQueue canardTxInit(
56+
const size_t capacity,
57+
const size_t mtu_bytes);
58+
```
59+
- **New Prototype**:
60+
```c
61+
struct CanardTxQueue canardTxInit(
62+
const size_t capacity,
63+
const size_t mtu_bytes,
64+
const struct CanardMemoryResource memory);
65+
```
66+
- **Changes**:
67+
- Adds a `CanardMemoryResource` parameter for memory allocation of payload data.
68+
69+
3. **`canardTxPush`**:
70+
- **Old Prototype**:
71+
```c
72+
int32_t canardTxPush(
73+
CanardTxQueue* const que,
74+
CanardInstance* const ins,
75+
const CanardMicrosecond tx_deadline_usec,
76+
const CanardTransferMetadata* const metadata,
77+
const size_t payload_size,
78+
const void* const payload);
79+
```
80+
- **New Prototype**:
81+
```c
82+
int32_t canardTxPush(
83+
struct CanardTxQueue* const que,
84+
const struct CanardInstance* const ins,
85+
const CanardMicrosecond tx_deadline_usec,
86+
const struct CanardTransferMetadata* const metadata,
87+
const struct CanardPayload payload);
88+
```
89+
- **Changes**:
90+
- Replaces `payload_size` and `payload` with a single `CanardPayload` struct.
91+
92+
4. **`canardTxPeek`** and **`canardTxPop`**:
93+
- The functions now return and accept pointers to mutable `struct CanardTxQueueItem` instead of const pointers.
94+
95+
### Removed Functions
96+
97+
- No functions have been explicitly removed, but some have modified prototypes.
98+
99+
## Data Structure Changes
100+
101+
### Type Definitions
102+
103+
- **Enumerations**:
104+
- Changed from `typedef enum` to `enum` without `typedef`.
105+
- **Old**:
106+
```c
107+
typedef enum { ... } CanardPriority;
108+
```
109+
- **New**:
110+
```c
111+
enum CanardPriority { ... };
112+
```
113+
114+
- **Structures**:
115+
- Changed from forward declarations and `typedef struct` to direct `struct` definitions.
116+
- **Old**:
117+
```c
118+
typedef struct CanardInstance CanardInstance;
119+
```
120+
- **New**:
121+
```c
122+
struct CanardInstance { ... };
123+
```
124+
125+
### Struct Modifications
126+
127+
- **`CanardFrame`**:
128+
- **Old**:
129+
```c
130+
typedef struct {
131+
uint32_t extended_can_id;
132+
size_t payload_size;
133+
const void* payload;
134+
} CanardFrame;
135+
```
136+
- **New**:
137+
```c
138+
struct CanardFrame {
139+
uint32_t extended_can_id;
140+
struct CanardPayload payload;
141+
};
142+
```
143+
- **Changes**:
144+
- Payload now uses the `CanardPayload` struct.
145+
146+
- **New Structs Introduced**:
147+
- **`CanardPayload`** and **`CanardMutablePayload`**: Encapsulate payload data and size.
148+
- **`CanardMutableFrame`**: Similar to `CanardFrame` but uses `CanardMutablePayload`.
149+
150+
- **`CanardInstance`**:
151+
- Now contains a `CanardMemoryResource` for memory management instead of separate function pointers.
152+
153+
- **`CanardTxQueue`**:
154+
- Includes a `CanardMemoryResource` for payload data allocation.
155+
156+
- **`CanardMemoryResource`** and **`CanardMemoryDeleter`**:
157+
- New structs to encapsulate memory allocation and deallocation functions along with user references.
158+
159+
## Memory Management Changes
160+
161+
- **Memory Resource Structs**:
162+
- Memory allocation and deallocation are now handled via `CanardMemoryResource` and `CanardMemoryDeleter` structs.
163+
- Functions now receive these structs instead of direct function pointers.
164+
165+
- **Allocation Functions**:
166+
- **Allocation**:
167+
```c
168+
typedef void* (*CanardMemoryAllocate)(
169+
void* const user_reference,
170+
const size_t size);
171+
```
172+
- **Deallocation**:
173+
```c
174+
typedef void (*CanardMemoryDeallocate)(
175+
void* const user_reference,
176+
const size_t size,
177+
void* const pointer);
178+
```
179+
180+
## Migration Steps
181+
182+
1. **Update Type Definitions**:
183+
- Replace all `typedef`-based enum and struct types with direct `struct` and `enum` declarations.
184+
- For example, change `CanardInstance` to `struct CanardInstance`.
185+
186+
2. **Adjust Memory Management Code**:
187+
- Replace separate memory allocation and deallocation function pointers with `CanardMemoryResource` and `CanardMemoryDeleter` structs.
188+
- Update function calls and definitions accordingly.
189+
190+
3. **Modify Function Calls**:
191+
- Update all function calls to match the new prototypes.
192+
- **`canardInit`**:
193+
- Before:
194+
```c
195+
canardInit(memory_allocate, memory_free);
196+
```
197+
- After:
198+
```c
199+
struct CanardMemoryResource memory = {
200+
.user_reference = ...,
201+
.allocate = memory_allocate,
202+
.deallocate = memory_free
203+
};
204+
canardInit(memory);
205+
```
206+
- **`canardTxInit`**:
207+
- Before:
208+
```c
209+
canardTxInit(capacity, mtu_bytes);
210+
```
211+
- After:
212+
```c
213+
canardTxInit(capacity, mtu_bytes, memory);
214+
```
215+
- **`canardTxPush`**:
216+
- Before:
217+
```c
218+
canardTxPush(que, ins, tx_deadline_usec, metadata, payload_size, payload);
219+
```
220+
- After:
221+
```c
222+
struct CanardPayload payload_struct = {
223+
.size = payload_size,
224+
.data = payload
225+
};
226+
canardTxPush(que, ins, tx_deadline_usec, metadata, payload_struct);
227+
```
228+
229+
4. **Handle New Functions**:
230+
- Use `canardTxFree` to deallocate transmission queue items after popping them.
231+
- Example:
232+
```c
233+
struct CanardTxQueueItem* item = canardTxPeek(&tx_queue);
234+
while (item != NULL) {
235+
// Transmit the frame...
236+
canardTxPop(&tx_queue, item);
237+
canardTxFree(&tx_queue, &canard_instance, item);
238+
item = canardTxPeek(&tx_queue);
239+
}
240+
```
241+
242+
5. **Update Struct Field Access**:
243+
- Adjust your code to access struct fields directly, considering the changes in struct definitions.
244+
- For example, access `payload.size` instead of `payload_size`.
245+
246+
6. **Adjust Memory Allocation Logic**:
247+
- Ensure that your memory allocation and deallocation functions conform to the new prototypes.
248+
- Pay attention to the additional `size` parameter in the deallocation function.
249+
250+
7. **Test Thoroughly**:
251+
- After making the changes, thoroughly test your application to ensure that it functions correctly with the new library version.
252+
- Pay special attention to memory management and potential leaks.

README.md

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ reused in the target application to speed up the design of the media IO layer (d
5757

5858
## Example
5959

60-
## TODO: Update to show how to use the new sized de-allocations, and how not to confuse payload_size and allocated_size.
61-
☝ todo will be addressed as soon as the new API is stable (tx, rx, and "zero copy" aspects).
62-
6360
The example augments the documentation but does not replace it.
6461

6562
The library requires a constant-complexity deterministic dynamic memory allocator.
@@ -68,39 +65,43 @@ so let's suppose that we're using [O1Heap](https://github.com/pavel-kirienko/o1h
6865
We are going to need basic wrappers:
6966

7067
```c
71-
static void* memAllocate(CanardInstance* const canard, const size_t amount)
68+
static void* memAllocate(void* const user_reference, const size_t size)
7269
{
73-
(void) canard;
74-
return o1heapAllocate(my_allocator, amount);
70+
(void) user_reference;
71+
return o1heapAllocate(my_allocator, size);
7572
}
7673

77-
static void memFree(CanardInstance* const canard, void* const pointer, const size_t amount)
74+
static void memFree(void* const user_reference, const size_t size, void* const pointer)
7875
{
79-
(void) canard;
80-
(void) amount;
76+
(void) user_reference;
77+
(void) size;
8178
o1heapFree(my_allocator, pointer);
8279
}
8380
```
8481
8582
Init a library instance:
8683
8784
```c
88-
CanardInstance canard = canardInit(&memAllocate, &memFree);
85+
const struct CanardMemoryResource memory{nullptr, &memAllocate, &memFree};
86+
struct CanardInstance canard = canardInit(memory);
8987
canard.node_id = 42; // Defaults to anonymous; can be set up later at any point.
9088
```
9189

9290
In order to be able to send transfers over the network, we will need one transmission queue per redundant CAN interface:
9391

9492
```c
95-
CanardTxQueue queue = canardTxInit(100, // Limit the size of the queue at 100 frames.
96-
CANARD_MTU_CAN_FD); // Set MTU = 64 bytes. There is also CANARD_MTU_CAN_CLASSIC.
93+
const struct CanardMemoryResource tx_memory{nullptr, memAllocate, memFree};
94+
struct CanardTxQueue queue = canardTxInit(
95+
100, // Limit the size of the queue at 100 frames.
96+
CANARD_MTU_CAN_FD, // Set MTU = 64 bytes. There is also CANARD_MTU_CAN_CLASSIC.
97+
tx_memory);
9798
```
9899
99100
Publish a message (message serialization not shown):
100101
101102
```c
102103
static uint8_t my_message_transfer_id; // Must be static or heap-allocated to retain state between calls.
103-
const CanardTransferMetadata transfer_metadata = {
104+
const struct CanardTransferMetadata transfer_metadata = {
104105
.priority = CanardPriorityNominal,
105106
.transfer_kind = CanardTransferKindMessage,
106107
.port_id = 1234, // This is the subject-ID.
@@ -131,7 +132,7 @@ Normally, the following fragment should be invoked periodically to unload the CA
131132
prioritized transmission queue (or several, if redundant network interfaces are used) into the CAN driver:
132133

133134
```c
134-
for (const CanardTxQueueItem* ti = NULL; (ti = canardTxPeek(&queue)) != NULL;) // Peek at the top of the queue.
135+
for (struct CanardTxQueueItem* ti = NULL; (ti = canardTxPeek(&queue)) != NULL;) // Peek at the top of the queue.
135136
{
136137
if ((0U == ti->tx_deadline_usec) || (ti->tx_deadline_usec > getCurrentMicroseconds())) // Check the deadline.
137138
{
@@ -141,7 +142,7 @@ for (const CanardTxQueueItem* ti = NULL; (ti = canardTxPeek(&queue)) != NULL;)
141142
}
142143
}
143144
// After the frame is transmitted or if it has timed out while waiting, pop it from the queue and deallocate:
144-
canard.memory_free(&canard, canardTxPop(&queue, ti));
145+
canardTxFree(&queue, &canard, canardTxPop(&queue, ti));
145146
}
146147
```
147148

@@ -150,15 +151,15 @@ from any of the redundant interfaces.
150151
But first, we need to subscribe:
151152

152153
```c
153-
CanardRxSubscription heartbeat_subscription;
154+
struct CanardRxSubscription heartbeat_subscription;
154155
(void) canardRxSubscribe(&canard, // Subscribe to messages uavcan.node.Heartbeat.
155156
CanardTransferKindMessage,
156157
7509, // The fixed Subject-ID of the Heartbeat message type (see DSDL definition).
157158
16, // The extent (the maximum possible payload size) provided by Nunavut.
158159
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
159160
&heartbeat_subscription);
160161

161-
CanardRxSubscription my_service_subscription;
162+
struct CanardRxSubscription my_service_subscription;
162163
(void) canardRxSubscribe(&canard, // Subscribe to an arbitrary service response.
163164
CanardTransferKindResponse, // Specify that we want service responses, not requests.
164165
123, // The Service-ID whose responses we will receive.
@@ -183,7 +184,7 @@ Normally, however, an embedded application would subscribe once and roll with it
183184
Okay, this is how we receive transfers:
184185

185186
```c
186-
CanardRxTransfer transfer;
187+
struct CanardRxTransfer transfer;
187188
const int8_t result = canardRxAccept(&canard,
188189
rx_timestamp_usec, // When the frame was received, in microseconds.
189190
&received_frame, // The CAN frame received from the bus.
@@ -216,15 +217,15 @@ the number of irrelevant transfers processed in software.
216217

217218
```c
218219
// Generate an acceptance filter to receive only uavcan.node.Heartbeat.1.0 messages (fixed port-ID 7509):
219-
CanardFilter heartbeat_config = canardMakeFilterForSubject(7509);
220+
struct CanardFilter heartbeat_config = canardMakeFilterForSubject(7509);
220221
// And to receive only uavcan.register.Access.1.0 service transfers (fixed port-ID 384):
221-
CanardFilter register_access_config = canardMakeFilterForService(384, ins.node_id);
222+
struct CanardFilter register_access_config = canardMakeFilterForService(384, ins.node_id);
222223

223224
// You can also combine the two filter configurations into one (may also accept irrelevant messages).
224225
// This allows consolidating a large set of configurations to fit the number of hardware filters.
225226
// For more information on the optimal subset of configurations to consolidate to minimize wasted CPU,
226227
// see the Cyphal specification.
227-
CanardFilter combined_config =
228+
struct CanardFilter combined_config =
228229
canardConsolidateFilters(&heartbeat_config, &register_access_config);
229230
configureHardwareFilters(combined_config.extended_can_id, combined_config.extended_mask);
230231
```
@@ -234,6 +235,11 @@ If you find the examples to be unclear or incorrect, please, open a ticket.
234235
235236
## Revisions
236237
238+
### v4.0
239+
240+
- Updating from Libcanard v3 to v4 involves several significant changes, especially in memory management and API function prototypes.
241+
- Please follow [MIGRATION_v3.x_to_v4.0](MIGRATION_v3.x_to_v4.0.md) guide and carefully update your code.
242+
237243
### v3.2
238244
239245
- Added new `canardRxGetSubscription`.

0 commit comments

Comments
 (0)