Skip to content

Commit de15924

Browse files
Update libcanard and fix socketcan, supersede 38 (#41)
* Update libcanard and fix socketcan * Fix clang-format and brackets * socketcan: Use CanardFilter instead of custom type * Cyphalize Signed-off-by: delphi <[email protected]> Co-authored-by: delphi <[email protected]>
1 parent 9f446aa commit de15924

File tree

8 files changed

+109
-107
lines changed

8 files changed

+109
-107
lines changed

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
MIT License
22

33
Copyright (c) 2018 UAVCAN
4+
Copyright (c) 2022 OpenCyphal
45

56
Permission is hereby granted, free of charge, to any person obtaining a copy
67
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# Platform-specific components
22

3-
[![Build Status](https://travis-ci.org/UAVCAN/platform_specific_components.svg?branch=master)](https://travis-ci.org/UAVCAN/platform_specific_components)
4-
[![Forum](https://img.shields.io/discourse/users.svg?server=https%3A%2F%2Fforum.uavcan.org&color=1700b3)](https://forum.uavcan.org)
3+
[![Main Workflow](https://github.com/OpenCyphal-Garage/platform_specific_components/actions/workflows/main.yml/badge.svg)](https://github.com/OpenCyphal-Garage/platform_specific_components/actions/workflows/main.yml)
4+
[![Forum](https://img.shields.io/discourse/users.svg?server=https%3A%2F%2Fforum.opencyphal.org&color=1700b3)](https://forum.opencyphal.org)
55

6-
This repository contains various platform-specific components maintained by the UAVCAN Development Team.
6+
This repository contains various platform-specific components maintained by the OpenCyphal team.
77
The quality and the level of support provided for these components may be substantially lower than
8-
that of the UAVCAN implementation libraries.
8+
that of the official OpenCyphal implementation libraries.
99

1010
The content is organized into directories following the pattern `/<platform>/<library>/`;
11-
for example, the Libuavcan driver for STM32 can be found under `/stm32/libuavcan/`.
11+
for example, the Libuavcan driver for STM32 can be found under `/stm32/libcanard/`.
1212
Further segregation may be defined on a per-directory basis.
1313

1414
All code is MIT-licensed unless a dedicated LICENSE file is provided.
@@ -21,5 +21,3 @@ All code is MIT-licensed unless a dedicated LICENSE file is provided.
2121
Users are encouraged to search through this repository for code that can be used with their target platform
2222
and use it as a starting point or as a practical guideline in the development of a customized solution for
2323
the specific application.
24-
If nothing is found, consider checking the `legacy-v0` branch: the code contained there may require heavy modification
25-
but it might still be useful.

socketcan/libcanard/src/socketcan.c

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// This software is distributed under the terms of the MIT License.
2-
/// Copyright (c) 2020 UAVCAN Development Team.
2+
/// Copyright (c) 2020 OpenCyphal
33
/// Authors: Pavel Kirienko <[email protected]>, Tom De Rybel <[email protected]>
44

55
// This is needed to enable the necessary declarations in sys/
@@ -164,12 +164,13 @@ int16_t socketcanPush(const SocketCANFD fd, const CanardFrame* const frame, cons
164164
return poll_result;
165165
}
166166

167-
int16_t socketcanPop(const SocketCANFD fd,
168-
CanardFrame* const out_frame,
169-
const size_t payload_buffer_size,
170-
void* const payload_buffer,
171-
const CanardMicrosecond timeout_usec,
172-
bool* const loopback)
167+
int16_t socketcanPop(const SocketCANFD fd,
168+
CanardFrame* const out_frame,
169+
CanardMicrosecond* const out_timestamp_usec,
170+
const size_t payload_buffer_size,
171+
void* const payload_buffer,
172+
const CanardMicrosecond timeout_usec,
173+
bool* const loopback)
173174
{
174175
if ((out_frame == NULL) || (payload_buffer == NULL))
175176
{
@@ -185,8 +186,8 @@ int16_t socketcanPop(const SocketCANFD fd,
185186
struct canfd_frame sockcan_frame = {0}; // CAN FD frame storage.
186187
struct iovec iov = {
187188
// Scatter/gather array items struct.
188-
.iov_base = &sockcan_frame, // Starting address.
189-
.iov_len = sizeof(sockcan_frame) // Number of bytes to transfer.
189+
.iov_base = &sockcan_frame, // Starting address.
190+
.iov_len = sizeof(sockcan_frame) // Number of bytes to transfer.
190191

191192
};
192193

@@ -244,22 +245,25 @@ int16_t socketcanPop(const SocketCANFD fd,
244245

245246
// Obtain the CAN frame time stamp from the kernel.
246247
// This time stamp is from the CLOCK_REALTIME kernel source.
247-
const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
248-
struct timeval tv = {0};
249-
assert(cmsg != NULL);
250-
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP)
248+
if (NULL != out_timestamp_usec)
251249
{
252-
(void) memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv)); // Copy to avoid alignment problems
253-
assert(tv.tv_sec >= 0 && tv.tv_usec >= 0);
250+
const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
251+
struct timeval tv = {0};
252+
assert(cmsg != NULL);
253+
if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP))
254+
{
255+
(void) memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv)); // Copy to avoid alignment problems
256+
assert(tv.tv_sec >= 0 && tv.tv_usec >= 0);
257+
}
258+
else
259+
{
260+
assert(0);
261+
return -EIO;
262+
}
263+
264+
(void) memset(out_frame, 0, sizeof(CanardFrame));
265+
*out_timestamp_usec = (CanardMicrosecond) (((uint64_t) tv.tv_sec * MEGA) + (uint64_t) tv.tv_usec);
254266
}
255-
else
256-
{
257-
assert(0);
258-
return -EIO;
259-
}
260-
261-
(void) memset(out_frame, 0, sizeof(CanardFrame));
262-
out_frame->timestamp_usec = (CanardMicrosecond)(((uint64_t) tv.tv_sec * MEGA) + (uint64_t) tv.tv_usec);
263267
out_frame->extended_can_id = sockcan_frame.can_id & CAN_EFF_MASK;
264268
out_frame->payload_size = sockcan_frame.len;
265269
out_frame->payload = payload_buffer;
@@ -268,7 +272,7 @@ int16_t socketcanPop(const SocketCANFD fd,
268272
return poll_result;
269273
}
270274

271-
int16_t socketcanFilter(const SocketCANFD fd, const size_t num_configs, const SocketCANFilterConfig* const configs)
275+
int16_t socketcanFilter(const SocketCANFD fd, const size_t num_configs, const CanardFilter* const configs)
272276
{
273277
if (configs == NULL)
274278
{
@@ -282,12 +286,12 @@ int16_t socketcanFilter(const SocketCANFD fd, const size_t num_configs, const So
282286
struct can_filter cfs[CAN_RAW_FILTER_MAX];
283287
for (size_t i = 0; i < num_configs; i++)
284288
{
285-
cfs[i].can_id = (configs[i].extended_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
286-
cfs[i].can_mask = (configs[i].mask & CAN_EFF_MASK) | CAN_EFF_FLAG | CAN_RTR_FLAG;
289+
cfs[i].can_id = (configs[i].extended_can_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
290+
cfs[i].can_mask = (configs[i].extended_mask & CAN_EFF_MASK) | CAN_EFF_FLAG | CAN_RTR_FLAG;
287291
}
288292

289293
const int ret =
290-
setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, cfs, (socklen_t)(sizeof(struct can_filter) * num_configs));
294+
setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, cfs, (socklen_t) (sizeof(struct can_filter) * num_configs));
291295

292296
return (ret < 0) ? getNegatedErrno() : 0;
293297
}

socketcan/libcanard/src/socketcan.h

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
/// __ __ _______ __ __ _______ _______ __ __
2-
/// | | | | / _ ` | | | | / ____| / _ ` | ` | |
3-
/// | | | | | |_| | | | | | | | | |_| | | `| |
4-
/// | |_| | | _ | ` `_/ / | |____ | _ | | |` |
5-
/// `_______/ |__| |__| `_____/ `_______| |__| |__| |__| `__|
6-
/// | | | | | |
7-
/// ----o------o------------o---------o------o---------o-------
1+
/// ____ ______ __ __
2+
/// / __ `____ ___ ____ / ____/_ ______ / /_ ____ / /
3+
/// / / / / __ `/ _ `/ __ `/ / / / / / __ `/ __ `/ __ `/ /
4+
/// / /_/ / /_/ / __/ / / / /___/ /_/ / /_/ / / / / /_/ / /
5+
/// `____/ .___/`___/_/ /_/`____/`__, / .___/_/ /_/`__,_/_/
6+
/// /_/ /____/_/
87
///
98
/// This is a basic adapter library that bridges Libcanard with SocketCAN.
109
/// Read the API documentation for usage information.
@@ -14,6 +13,8 @@
1413
/// --------------------------------------------------------------------------------------------------------------------
1514
/// Changelog
1615
///
16+
/// v3.0 - Update for compatibility with Libcanard v3.
17+
///
1718
/// v2.0 - Added loop-back functionality.
1819
/// API change in socketcanPop(): loopback flag added.
1920
/// - Changed to kernel-based time-stamping for received frames for improved accuracy.
@@ -23,7 +24,7 @@
2324
/// --------------------------------------------------------------------------------------------------------------------
2425
///
2526
/// This software is distributed under the terms of the MIT License.
26-
/// Copyright (c) 2020 UAVCAN Development Team.
27+
/// Copyright (c) 2020 OpenCyphal
2728
/// Author: Pavel Kirienko <[email protected]>
2829

2930
#ifndef SOCKETCAN_H_INCLUDED
@@ -64,26 +65,19 @@ int16_t socketcanPush(const SocketCANFD fd, const CanardFrame* const frame, cons
6465
/// The function will block until a frame is received or until the timeout is expired. It may return early.
6566
/// Zero timeout makes the operation non-blocking.
6667
/// Returns 1 on success, 0 on timeout, negated errno on error.
67-
int16_t socketcanPop(const SocketCANFD fd,
68-
CanardFrame* const out_frame,
69-
const size_t payload_buffer_size,
70-
void* const payload_buffer,
71-
const CanardMicrosecond timeout_usec,
72-
bool* const loopback);
73-
74-
/// The configuration of a single extended 29-bit data frame acceptance filter.
75-
/// Bits above the 29-th shall be cleared.
76-
typedef struct SocketCANFilterConfig
77-
{
78-
uint32_t extended_id;
79-
uint32_t mask;
80-
} SocketCANFilterConfig;
68+
int16_t socketcanPop(const SocketCANFD fd,
69+
CanardFrame* const out_frame,
70+
CanardMicrosecond* const out_timestamp_usec,
71+
const size_t payload_buffer_size,
72+
void* const payload_buffer,
73+
const CanardMicrosecond timeout_usec,
74+
bool* const loopback);
8175

8276
/// Apply the specified acceptance filter configuration.
8377
/// Note that it is only possible to accept extended-format data frames.
8478
/// The default configuration is to accept everything.
8579
/// Returns 0 on success, negated errno on error.
86-
int16_t socketcanFilter(const SocketCANFD fd, const size_t num_configs, const SocketCANFilterConfig* const configs);
80+
int16_t socketcanFilter(const SocketCANFD fd, const size_t num_configs, const CanardFilter* const configs);
8781

8882
#ifdef __cplusplus
8983
}

socketcan/libcanard/test/test_io.cpp

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// This software is distributed under the terms of the MIT License.
2-
/// Copyright (c) 2020 UAVCAN Development Team.
2+
/// Copyright (c) 2020 OpenCyphal
33
/// Author: Pavel Kirienko <[email protected]>
44

55
#include "socketcan.h"
@@ -16,7 +16,8 @@ TEST_CASE("IO-Classic") // Catch2 does not support parametrized tests yet.
1616
REQUIRE(sa >= 0);
1717
REQUIRE(sb >= 0);
1818

19-
CanardFrame fr{};
19+
CanardFrame fr{};
20+
CanardMicrosecond timestamp_usec{};
2021
fr.extended_can_id = 0x1234U;
2122
fr.payload_size = 6;
2223
fr.payload = "Hello";
@@ -29,26 +30,26 @@ TEST_CASE("IO-Classic") // Catch2 does not support parametrized tests yet.
2930

3031
char buf[255]{};
3132
fr = {};
32-
REQUIRE(1 == socketcanPop(sb, &fr, sizeof(buf), buf, 1000, nullptr));
33-
REQUIRE(fr.timestamp_usec > 0);
33+
REQUIRE(1 == socketcanPop(sb, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr));
34+
REQUIRE(timestamp_usec > 0);
3435
REQUIRE(fr.extended_can_id == 0x1234U);
3536
REQUIRE(fr.payload_size == 6);
3637
REQUIRE(0 == std::memcmp(fr.payload, "Hello", 6));
37-
auto old_ts = fr.timestamp_usec;
38+
auto old_ts = timestamp_usec;
3839

3940
fr = {};
40-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, nullptr)); // Loopback frame.
41-
REQUIRE(1 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, nullptr)); // Received actual frame.
42-
REQUIRE(fr.timestamp_usec > 0);
43-
REQUIRE(fr.timestamp_usec >= old_ts);
41+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr)); // Loopback frame.
42+
REQUIRE(1 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr)); // Received actual frame.
43+
REQUIRE(timestamp_usec > 0);
44+
REQUIRE(timestamp_usec >= old_ts);
4445
REQUIRE(fr.extended_can_id == 0x4321U);
4546
REQUIRE(fr.payload_size == 7);
4647
REQUIRE(0 == std::memcmp(fr.payload, "World!", 7));
4748

48-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 0, nullptr));
49-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, nullptr));
50-
REQUIRE(-EINVAL == socketcanPop(sa, &fr, 0, nullptr, 1000, nullptr));
51-
REQUIRE(-EINVAL == socketcanPop(sa, nullptr, sizeof(buf), buf, 1000, nullptr));
49+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 0, nullptr));
50+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr));
51+
REQUIRE(-EINVAL == socketcanPop(sa, &fr, &timestamp_usec, 0, nullptr, 1000, nullptr));
52+
REQUIRE(-EINVAL == socketcanPop(sa, nullptr, nullptr, sizeof(buf), buf, 1000, nullptr));
5253

5354
REQUIRE(-EINVAL == socketcanPush(sa, nullptr, 1'000'000));
5455

@@ -65,15 +66,16 @@ TEST_CASE("IO-FD")
6566
REQUIRE(sa >= 0);
6667
REQUIRE(sb >= 0);
6768

68-
const SocketCANFilterConfig fcs = {
69+
const CanardFilter fcs = {
6970
0x00001234U,
7071
0x1FFFFFFFU,
7172
};
7273
REQUIRE(0 == socketcanFilter(sb, 1, &fcs));
7374
REQUIRE(-EFBIG == socketcanFilter(sa, 1'000'000, &fcs));
7475
REQUIRE(-EINVAL == socketcanFilter(sa, 1, NULL));
7576

76-
CanardFrame fr{};
77+
CanardFrame fr{};
78+
CanardMicrosecond timestamp_usec{};
7779
fr.extended_can_id = 0x1234U;
7880
fr.payload_size = 13;
7981
fr.payload = "Hello world!";
@@ -86,26 +88,26 @@ TEST_CASE("IO-FD")
8688

8789
char buf[255]{};
8890
fr = {};
89-
REQUIRE(1 == socketcanPop(sb, &fr, sizeof(buf), buf, 1000, nullptr));
90-
REQUIRE(fr.timestamp_usec > 0);
91+
REQUIRE(1 == socketcanPop(sb, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr));
92+
REQUIRE(timestamp_usec > 0);
9193
REQUIRE(fr.extended_can_id == 0x1234U);
9294
REQUIRE(fr.payload_size == 13);
9395
REQUIRE(0 == std::memcmp(fr.payload, "Hello world!", 13));
94-
auto old_ts = fr.timestamp_usec;
96+
auto old_ts = timestamp_usec;
9597

9698
fr = {};
97-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, nullptr)); // Loopback frame.
98-
REQUIRE(1 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, nullptr)); // Received actual frame.
99-
REQUIRE(fr.timestamp_usec > 0);
100-
REQUIRE(fr.timestamp_usec >= old_ts);
99+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr)); // Loopback frame.
100+
REQUIRE(1 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr)); // Received actual frame.
101+
REQUIRE(timestamp_usec > 0);
102+
REQUIRE(timestamp_usec >= old_ts);
101103
REQUIRE(fr.extended_can_id == 0x4321U);
102104
REQUIRE(fr.payload_size == 10);
103105
REQUIRE(0 == std::memcmp(fr.payload, "0123456789", 10));
104106

105-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 0, nullptr));
106-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, nullptr));
107-
REQUIRE(-EINVAL == socketcanPop(sa, &fr, 0, nullptr, 1000, nullptr));
108-
REQUIRE(-EINVAL == socketcanPop(sa, nullptr, sizeof(buf), buf, 1000, nullptr));
107+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 0, nullptr));
108+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr));
109+
REQUIRE(-EINVAL == socketcanPop(sa, &fr, &timestamp_usec, 0, nullptr, 1000, nullptr));
110+
REQUIRE(-EINVAL == socketcanPop(sa, nullptr, nullptr, sizeof(buf), buf, 1000, nullptr));
109111

110112
REQUIRE(-EINVAL == socketcanPush(sa, nullptr, 1'000'000));
111113

@@ -122,7 +124,8 @@ TEST_CASE("IO-FD-Loopback")
122124
REQUIRE(sa >= 0);
123125
REQUIRE(sb >= 0);
124126

125-
CanardFrame fr{};
127+
CanardFrame fr{};
128+
CanardMicrosecond timestamp_usec{};
126129
fr.extended_can_id = 0x1234U;
127130
fr.payload_size = 13;
128131
fr.payload = "Hello World!";
@@ -131,25 +134,28 @@ TEST_CASE("IO-FD-Loopback")
131134
bool loopback = true;
132135
char buf[255]{};
133136
fr = {};
134-
REQUIRE(1 == socketcanPop(sb, &fr, sizeof(buf), buf, 1000, &loopback)); // Receive actual frame on sb.
137+
REQUIRE(1 ==
138+
socketcanPop(sb, &fr, &timestamp_usec, sizeof(buf), buf, 1000, &loopback)); // Receive actual frame on sb.
135139
REQUIRE(loopback == false);
136-
REQUIRE(fr.timestamp_usec > 0);
140+
REQUIRE(timestamp_usec > 0);
137141
REQUIRE(fr.extended_can_id == 0x1234U);
138142
REQUIRE(fr.payload_size == 13);
139143
REQUIRE(0 == std::memcmp(fr.payload, "Hello World!", 13));
140-
auto old_ts = fr.timestamp_usec;
144+
auto old_ts = timestamp_usec;
141145

142146
fr = {};
143-
REQUIRE(1 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, &loopback)); // Receive loopback frame on sa.
147+
REQUIRE(
148+
1 ==
149+
socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, &loopback)); // Receive loopback frame on sa.
144150
REQUIRE(loopback == true);
145-
REQUIRE(fr.timestamp_usec > 0);
146-
REQUIRE(fr.timestamp_usec >= old_ts);
151+
REQUIRE(timestamp_usec > 0);
152+
REQUIRE(timestamp_usec >= old_ts);
147153
REQUIRE(fr.extended_can_id == 0x1234U);
148154
REQUIRE(fr.payload_size == 13);
149155
REQUIRE(0 == std::memcmp(fr.payload, "Hello World!", 13));
150156

151-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 0, nullptr)); // No more frames.
152-
REQUIRE(0 == socketcanPop(sa, &fr, sizeof(buf), buf, 1000, nullptr)); // No more frames.
157+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 0, nullptr)); // No more frames.
158+
REQUIRE(0 == socketcanPop(sa, &fr, &timestamp_usec, sizeof(buf), buf, 1000, nullptr)); // No more frames.
153159

154160
::close(sa);
155161
::close(sb);

stm32/libcanard/bxcan/src/bxcan.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// This software is distributed under the terms of the MIT License.
2-
/// Copyright (c) 2016-2020 UAVCAN Development Team.
2+
/// Copyright (c) 2016-2022 OpenCyphal
33
/// Authors: Pavel Kirienko <[email protected]>, Tom De Rybel <[email protected]>
44

55
#include "bxcan.h"
@@ -294,7 +294,7 @@ bool bxCANConfigure(const uint8_t iface_index, //
294294
//
295295
// Filters are alternating between FIFO0 and FIFO1 in order to equalize the load. (Set in FFA1R.)
296296
// This will cause occasional priority inversion and frame reordering on reception,
297-
// but that is acceptable for UAVCAN, and a majority of other protocols will tolerate
297+
// but that is acceptable for Cyphal/CAN, and a majority of other protocols will tolerate
298298
// this too, since there will be no reordering within the same CAN ID.
299299
if (iface_index == 0U)
300300
{

0 commit comments

Comments
 (0)