Libsockcanpp
A complete C++ wrapper around socketcan.
Loading...
Searching...
No Matches
CanDriver.cpp
Go to the documentation of this file.
1/**
2 * @file CanDriver.cpp
3 * @author Simon Cahill (contact@simonc.eu)
4 * @brief
5 * @version 0.1
6 * @date 2020-07-01
7 *
8 * @copyright Copyright (c) 2020-2025 Simon Cahill
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 */
22
23//////////////////////////////
24// SYSTEM INCLUDES //
25//////////////////////////////
26
27#include <fcntl.h>
28#include <linux/can.h>
29#include <linux/can/raw.h>
30#include <net/if.h>
31#include <sys/ioctl.h>
32#include <sys/socket.h>
33#include <sys/types.h>
34#include <unistd.h>
35
36#include <cmath>
37#include <cstdlib>
38#include <cstring>
39#include <mutex>
40#include <queue>
41#include <string>
42#include <thread>
43#include <vector>
44
45//////////////////////////////
46// LOCAL INCLUDES //
47//////////////////////////////
48#include "CanDriver.hpp"
49#include "CanId.hpp"
50#include "CanMessage.hpp"
51#include "exceptions/CanCloseException.hpp"
52#include "exceptions/CanException.hpp"
53#include "exceptions/CanInitException.hpp"
54#include "exceptions/InvalidSocketException.hpp"
55
56namespace sockcanpp {
57
58 using exceptions::CanCloseException;
59 using exceptions::CanException;
60 using exceptions::CanInitException;
61 using exceptions::InvalidSocketException;
62
63 using std::mutex;
64 using std::queue;
65 using std::string;
66 using std::strncpy;
67 using std::unique_lock;
68 using std::unique_ptr;
69 using std::chrono::milliseconds;
70 using std::this_thread::sleep_for;
71 using std::vector;
72
73 //////////////////////////////////////
74 // PUBLIC IMPLEMENTATION //
75 //////////////////////////////////////
76
77
78#pragma region "Object Construction"
79 CanDriver::CanDriver(const string& canInterface, int32_t canProtocol, const CanId defaultSenderId):
80 CanDriver(canInterface, canProtocol, 0 /* match all */, defaultSenderId) { }
81
82 CanDriver::CanDriver(const string& canInterface, const int32_t canProtocol, const int32_t filterMask, const CanId defaultSenderId):
83 CanDriver(canInterface, canProtocol, filtermap_t{{0, filterMask}}, defaultSenderId) { }
84
85 CanDriver::CanDriver(const string& canInterface, const int32_t canProtocol, const filtermap_t& filters, const CanId defaultSenderId):
86 _defaultSenderId(defaultSenderId), _canFilterMask(filters), _canProtocol(canProtocol), _canInterface(canInterface) {
88 }
89#pragma endregion
90
91#pragma region "I / O"
92 /**
93 * @brief Blocks until one or more CAN messages appear on the bus, or until the timeout runs out.
94 *
95 * @param timeout The time (in µsec) to wait before timing out.
96 *
97 * @return true If messages are available on the bus.
98 * @return false Otherwise.
99 */
100 bool CanDriver::waitForMessages(microseconds timeout/* = microseconds(3000)*/) {
101 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
102
103 unique_lock<mutex> locky(_lock);
104
105 fd_set readFileDescriptors;
106 timeval waitTime{0, static_cast<suseconds_t>(timeout.count())};
107
108 FD_ZERO(&readFileDescriptors);
109 FD_SET(_socketFd, &readFileDescriptors);
110 const auto fdsAvailable = select(_socketFd + 1, &readFileDescriptors, nullptr, nullptr, &waitTime);
111
112 int32_t bytesAvailable{0};
113 const auto retCode = ioctl(_socketFd, FIONREAD, &bytesAvailable);
114 if (retCode == 0) {
115 _queueSize = static_cast<int32_t>(std::ceil(bytesAvailable / sizeof(can_frame)));
116 } else {
117 _queueSize = 0;
118 // vcan interfaces don't support FIONREAD. So fall back to
119 // using alternate implementation in readQueuedMessages().
120 _canReadQueueSize = false;
121 }
122
123 return fdsAvailable > 0;
124 }
125
126 /**
127 * @brief Blocks until one or more CAN messages appear on the bus, or until the timeout runs out.
128 *
129 * @param timeout The time (in millis) to wait before timing out.
130 *
131 * @return true If messages are available on the bus.
132 * @return false Otherwise.
133 */
134 bool CanDriver::waitForMessages(milliseconds timeout) { return waitForMessages(std::chrono::duration_cast<microseconds>(timeout)); }
135
136 /**
137 * @brief Blocks until one or more CAN messages appear on the bus, or until the timeout runs out.
138 *
139 * @param timeout The time (in nanoseconds) to wait before timing out.
140 *
141 * @return true If messages are available on the bus.
142 * @return false Otherwise.
143 */
144 bool CanDriver::waitForMessages(nanoseconds timeout/* = nanoseconds(3000)*/) { return waitForMessages(std::chrono::duration_cast<microseconds>(timeout)); }
145
146 /**
147 * @brief Attempts to read a message from the associated CAN bus.
148 *
149 * @return CanMessage The message read from the bus.
150 */
152
153 /**
154 * @brief readMessage deadlock guard, attempts to read a message from the associated CAN bus.
155 *
156 * @return CanMessage The message read from the bus.
157 */
159 unique_ptr<unique_lock<mutex>> locky{nullptr};
160
161 if (lock) { locky = unique_ptr<unique_lock<mutex>>{new unique_lock<mutex>{_lock}}; }
162
163 if (0 > _socketFd) { throw InvalidSocketException("Invalid socket!", _socketFd); }
164
165 ssize_t readBytes{0};
166 can_frame canFrame{};
167
168 readBytes = read(_socketFd, &canFrame, sizeof(can_frame));
169
170 if (0 > readBytes) { throw CanException(formatString("FAILED to read from CAN! Error: %d => %s", errno, strerror(errno)), _socketFd); }
171
172 return CanMessage{canFrame};
173 }
174
175 /**
176 * @brief Attempts to send a CAN message on the associated bus.
177 *
178 * @param message The message to be sent.
179 * @param forceExtended Whether or not to force use of an extended ID.
180 *
181 * @return ssize_t The amount of bytes sent on the bus.
182 */
183 ssize_t CanDriver::sendMessage(const CanMessage& message, bool forceExtended) {
184 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
185
186 unique_lock<mutex> locky(_lockSend);
187
188 ssize_t bytesWritten = 0;
189
190 if (message.getFrameData().size() > CAN_MAX_DATA_LENGTH) {
191 throw CanException(formatString("INVALID data length! Message must be smaller than %d bytes!", CAN_MAX_DATA_LENGTH), _socketFd);
192 }
193
194 auto canFrame = message.getRawFrame();
195
196 if (forceExtended || ((uint32_t)message.getCanId() > CAN_SFF_MASK)) { canFrame.can_id |= CAN_EFF_FLAG; }
197
198 bytesWritten = write(_socketFd, (const void*)&canFrame, sizeof(canFrame));
199
200 if (bytesWritten == -1) { throw CanException(formatString("FAILED to write data to socket! Error: %d => %s", errno, strerror(errno)), _socketFd); }
201
202 return bytesWritten;
203 }
204
205 /**
206 * @brief Attempts to send a queue of messages on the associated CAN bus.
207 *
208 * @param messages A queue containing the messages to be sent.
209 * @param delay If greater than 0, will delay the sending of the next message.
210 * @param forceExtended Whether or not to force use of an extended ID.
211 *
212 * @return int32_t The total amount of bytes sent.
213 */
214 ssize_t CanDriver::sendMessageQueue(queue<CanMessage>& messages, microseconds delay, bool forceExtended) { return sendMessageQueue(messages, std::chrono::duration_cast<nanoseconds>(delay), forceExtended); }
215
216 /**
217 * @brief Attempts to send a queue of messages on the associated CAN bus.
218 *
219 * @param messages A queue containing the messages to be sent.
220 * @param delay If greater than 0, will delay the sending of the next message.
221 * @param forceExtended Whether or not to force use of an extended ID.
222 *
223 * @return int32_t The total amount of bytes sent.
224 */
225 ssize_t CanDriver::sendMessageQueue(queue<CanMessage>& messages, milliseconds delay, bool forceExtended) { return sendMessageQueue(messages, std::chrono::duration_cast<nanoseconds>(delay), forceExtended); }
226
227 /**
228 * @brief Attempts to send a queue of messages on the associated CAN bus.
229 *
230 * @param messages A queue containing the messages to be sent.
231 * @param delay If greater than 0, will delay the sending of the next message.
232 * @param forceExtended Whether or not to force use of an extended ID.
233 *
234 * @return int32_t The total amount of bytes sent.
235 */
236 ssize_t CanDriver::sendMessageQueue(queue<CanMessage>& messages, nanoseconds delay, bool forceExtended) {
237 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
238
239 ssize_t totalBytesWritten = 0;
240
241 while (!messages.empty()) {
242 totalBytesWritten += sendMessage(messages.front(), forceExtended);
243 messages.pop();
244
245 if (delay.count() > 0) {
246 sleep_for(delay);
247 }
248 }
249
250 return totalBytesWritten;
251 }
252
253 /**
254 * @brief Attempts to read all messages stored in the buffer for the associated CAN bus.
255 *
256 * @return queue<CanMessage> A queue containing the messages read from the bus buffer.
257 */
259 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
260
261 unique_lock<mutex> locky{_lock};
262 queue<CanMessage> messages{};
263
264 if (_canReadQueueSize) {
265 for (int32_t i = _queueSize; 0 < i; --i) {
266 messages.emplace(readMessageLock(false));
267 }
268 } else {
269 // If the interface doesn't support FIONREAD, fall back
270 // to reading until data exhausted.
271 bool more{true};
272
273 do {
274 ssize_t readBytes;
275 can_frame canFrame{};
276 readBytes = read(_socketFd, &canFrame, sizeof(can_frame));
277 if (readBytes >= 0) {
278 messages.emplace(canFrame);
279 } else if (errno == EAGAIN) {
280 more = false;
281 } else {
282 throw CanException(formatString("FAILED to read from CAN! Error: %d => %s", errno, strerror(errno)), _socketFd);
283 }
284 } while (more);
285 }
286
287 return messages;
288 }
289
290 /**
291 * @brief Sets the CAN FD frame option for the interface.
292 *
293 * This option allows the current driver instance to receive CAN FD frames.
294 *
295 * @param enabled Whether or not to enable the CAN FD frame option.
296 */
297 void CanDriver::allowCanFdFrames(const bool enabled/* = true*/) const {
298 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
299
300 int32_t canFdFrames = enabled ? 1 : 0;
301
302 if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canFdFrames, sizeof(canFdFrames)) == -1) {
303 throw CanInitException(formatString("FAILED to set CAN FD frames on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
304 }
305 }
306
307 #ifdef CANXL_XLF
308 /**
309 * @brief Sets the CAN XL option for the interface.
310 *
311 * This option allows the current driver instance to send/receive CAN XL frames.
312 *
313 * @param enabled Whether or not to enable the CAN XL option.
314 */
315 void CanDriver::allowCanXlFrames(const bool enabled/* = true*/) const {
316 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
317
318 int32_t canXlFrames = enabled ? 1 : 0;
319
320
321 if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_XL_FRAMES, &canXlFrames, sizeof(canXlFrames)) == -1) {
322 throw CanInitException(formatString("FAILED to set CAN XL frames on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
323 }
324 }
325 #endif // CANXL_XLF
326
327 /**
328 * @brief Configures the socket to join the CAN filters.
329 * This is especially required, when using inverted CAN filters.
330 *
331 * Source: https://stackoverflow.com/a/57680496/2921426
332 */
333 void CanDriver::joinCanFilters() const {
334 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
335
336 int32_t joinFilters = 1;
337
338 if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS, &joinFilters, sizeof(joinFilters)) == -1) {
339 throw CanInitException(formatString("FAILED to join CAN filters on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
340 }
341 }
342
343 /**
344 * @brief Attempts to set the filter mask for the associated CAN bus.
345 *
346 * @param mask The bit mask to apply.
347 * @param filterId The ID to filter on.
348 */
349 void CanDriver::setCanFilterMask(const int32_t mask, const CanId& filterId) { setCanFilters({{filterId, static_cast<uint32_t>(mask)}}); }
350
351 /**
352 * @brief Sets multiple CAN filters for the associated CAN bus.
353 *
354 * @param filters A map containing the filters to apply.
355 */
356 void CanDriver::setCanFilters(const filtermap_t& filters) {
357 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
358
359 unique_lock<mutex> locky(_lock);
360 vector<can_filter> canFilters{};
361
362 // Structured bindings only available with C++17
363 #if __cplusplus >= 201703L
364 for (const auto [id, filter] : filters) {
365 canFilters.push_back({*id, filter});
366 }
367 #else
368 for (const auto& filterPair : filters) {
369 canFilters.push_back({*filterPair.first, filterPair.second});
370 }
371 #endif
372
373 if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_FILTER, canFilters.data(), canFilters.size() * sizeof(can_filter)) == -1) {
374 throw CanInitException(formatString("FAILED to set CAN filters on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
375 }
376 }
377
378 /**
379 * @brief Sets the error filter for the associated CAN bus.
380 *
381 * @param enabled Whether or not to enable the error filter.
382 */
383 void CanDriver::setErrorFilter(const bool enabled/* = true*/) const {
384 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
385
386 int32_t errorFilter = enabled ? 1 : 0;
387
388 if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &errorFilter, sizeof(errorFilter)) == -1) {
389 throw CanInitException(formatString("FAILED to set CAN error filter on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
390 }
391 }
392
393 /**
394 * @brief Sets the receive own messages option for the associated CAN bus.
395 *
396 * This option allows the socket to receive its own messages.
397 *
398 * @param enabled Whether or not to enable the receive own messages option.
399 */
400 void CanDriver::setReceiveOwnMessages(const bool enabled) const {
401 if (_socketFd < 0) { throw InvalidSocketException("Invalid socket!", _socketFd); }
402
403 int32_t receiveOwnMessages = enabled ? 1 : 0;
404
405 if (setsockopt(_socketFd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &receiveOwnMessages, sizeof(receiveOwnMessages)) == -1) {
406 throw CanInitException(formatString("FAILED to set CAN error filter on socket %d! Error: %d => %s", _socketFd, errno, strerror(errno)));
407 }
408 }
409#pragma endregion
410
411 //////////////////////////////////////
412 // PROTECTED IMPLEMENTATION //
413 //////////////////////////////////////
414
415#pragma region Socket Management
416
417 /**
418 * @brief Initialises the underlying CAN socket.
419 */
421 struct sockaddr_can address{};
422 struct ifreq ifaceRequest{};
423 int64_t fdOptions{0};
424 int32_t tmpReturn{0};
425
426 _socketFd = socket(PF_CAN, SOCK_RAW, _canProtocol);
427
428 if (_socketFd == -1) {
429 throw CanInitException(formatString("FAILED to initialise socketcan! Error: %d => %s", errno, strerror(errno)));
430 }
431
432 std::copy(_canInterface.begin(), _canInterface.end(), ifaceRequest.ifr_name);
433
434 if ((tmpReturn = ioctl(_socketFd, SIOCGIFINDEX, &ifaceRequest)) == -1) {
435 throw CanInitException(formatString("FAILED to perform IO control operation on socket %s! Error: %d => %s", _canInterface.c_str(), errno,
436 strerror(errno)));
437 }
438
439 fdOptions = fcntl(_socketFd, F_GETFL);
440 fdOptions |= O_NONBLOCK;
441 tmpReturn = fcntl(_socketFd, F_SETFL, fdOptions);
442
443 address.can_family = AF_CAN;
444 address.can_ifindex = ifaceRequest.ifr_ifindex;
445
447
448 if ((tmpReturn = bind(_socketFd, (struct sockaddr*)&address, sizeof(address))) == -1) {
449 throw CanInitException(formatString("FAILED to bind to socket CAN! Error: %d => %s", errno, strerror(errno)));
450 }
451 }
452
453 /**
454 * @brief Closes the underlying CAN socket.
455 */
457 unique_lock<mutex> locky(_lock);
458
459 if (_socketFd <= 0) { throw CanCloseException("Cannot close invalid socket!"); }
460
461 if (close(_socketFd) == -1) { throw CanCloseException(formatString("FAILED to close CAN socket! Error: %d => %s", errno, strerror(errno))); }
462
463 _socketFd = -1;
464 }
465#pragma endregion
466
467} // namespace sockcanpp
CanDriver class; handles communication via CAN.
Definition CanDriver.hpp:68
virtual void allowCanFdFrames(const bool enabled=true) const
Sets the CAN FD frame option for the interface.
virtual void joinCanFilters() const
Configures the socket to join the CAN filters.
CanDriver(const string &canInterface, const int32_t canProtocol, const filtermap_t &filters, const CanId defaultSenderId=0)
Definition CanDriver.cpp:85
bool _canReadQueueSize
!< The size of the message queue read by waitForMessages()
virtual void setReceiveOwnMessages(const bool enabled=true) const
Sets the receive own messages option for the interface.
virtual CanMessage readMessage()
Attempts to read a single message from the bus.
virtual void setErrorFilter(const bool enabled=true) const
Sets the error filter for the interface.
CanDriver(const string &canInterface, const int32_t canProtocol, const int32_t filterMask, const CanId defaultSenderId=0)
Definition CanDriver.cpp:82
virtual ssize_t sendMessageQueue(queue< CanMessage > &messages, milliseconds delay=20ms, bool forceExtended=false)
Attempts to send a queue of messages.
virtual void setCanFilters(const filtermap_t &filters)
Sets the CAN filters for the interface.
string _canInterface
The CAN interface used for communication (e.g. can0, can1, ...)
CanId _defaultSenderId
The ID to send messages with if no other ID was set.
virtual queue< CanMessage > readQueuedMessages()
Attempts to read all queued messages from the bus.
int32_t _canProtocol
The protocol used when communicating via CAN.
virtual void setCanFilterMask(const int32_t mask, const CanId &filterId)
Attempts to set a new CAN filter mask to the interface.
virtual CanMessage readMessageLock(bool const lock=true)
readMessage deadlock guard
virtual ssize_t sendMessage(const CanMessage &message, bool forceExtended=false)
Attempts to send a single CAN message.
virtual ssize_t sendMessageQueue(queue< CanMessage > &messages, nanoseconds delay=20ns, bool forceExtended=false)
Attempts to send a queue of messages.
int32_t _socketFd
The CAN socket file descriptor.
virtual bool waitForMessages(milliseconds timeout=3000ms)
Waits for CAN messages to appear.
mutex _lock
!< Is the queue size available
filtermap_t _canFilterMask
The bit mask used to filter CAN messages.
virtual void initialiseSocketCan()
Initialises socketcan.
static constexpr int32_t CAN_MAX_DATA_LENGTH
The maximum amount of bytes allowed in a single CAN frame.
Definition CanDriver.hpp:70
virtual bool waitForMessages(nanoseconds timeout=3000ns)
Waits for CAN messages to appear.
virtual bool waitForMessages(microseconds timeout=3000us)
Waits for CAN messages to appear.
virtual ssize_t sendMessageQueue(queue< CanMessage > &messages, microseconds delay=20us, bool forceExtended=false)
Attempts to send a queue of messages.
virtual void uninitialiseSocketCan()
Uninitialises socketcan.
Represents a CAN message that was received.
const string getFrameData() const
const can_frame getRawFrame() const
CanMessage(const struct can_frame frame)
const CanId getCanId() const
An exception that may be thrown when an error occurs while closing a CAN socket.
An exception that may be thrown when an error occurs while closing a CAN socket.
CanException(const string &message, int32_t socket)
An exception that may be thrown when an error occurred while initialising a CAN socket.
An exception that may be thrown when an error occurs while closing a CAN socket.
InvalidSocketException(const string &message, int32_t socket)
Main library namespace.
Definition CanDriver.cpp:56
string formatString(const string &format, Args... args)
Formats a std string object.
Represents a CAN ID in a simple and easy-to-use manner.
Definition CanId.hpp:61
constexpr canid_t operator*() const
Returns the raw CAN ID value.
Definition CanId.hpp:75