Added initial support for CAN FD on Linux

This commit is contained in:
fpagliughi
2026-01-02 18:49:28 -05:00
parent 99a92afa88
commit bf5fa4b652
8 changed files with 297 additions and 54 deletions

7
.gitignore vendored
View File

@@ -2,6 +2,7 @@
build/
obj/
lib/
book/
# Compiled Object files
*.slo
@@ -41,10 +42,12 @@ lib/
.hgignore
.hgtags
# SlickEdit files
# Various editor and IDE files
sockpp.v*
*.vpj
.vscode/
# Misc
fmt.sh
.vscode/

View File

@@ -50,7 +50,8 @@
#include <linux/can.h>
#include <cstring>
#include <string>
// #include <string>
#include <algorithm>
#include "sockpp/platform.h"
#include "sockpp/types.h"
@@ -78,6 +79,11 @@ public:
* The frame is initialized to all zeroes.
*/
can_frame() : base{} {}
/**
* Constructs a frame with the specified ID and no data.
* @param canID The CAN identifier for the frame
*/
can_frame(canid_t canID) : can_frame{canID, nullptr, 0} {}
/**
* Constructs a frame with the specified ID and data.
* @param canID The CAN identifier for the frame
@@ -93,14 +99,159 @@ public:
*/
can_frame(canid_t canID, const void* data, size_t n) : base{} {
this->can_id = canID;
this->can_dlc = n;
::memcpy(&this->data, data, n);
if (data && n != 0) {
n = std::min(n, size_t(CAN_MAX_DLEN));
this->can_dlc = n;
::memcpy(&this->data, data, n);
}
}
/**
* Construct a frame from a C library CAN frame.
* @param frame A C lib CAN frame.
*/
can_frame(const base& frame) : base{frame} {}
/**
* Determines if this frame has an extended (29-bit) CAN ID.
* @return @em true if this frame has an extended ID, @em false if not
*/
bool has_extended_id() const { return (can_id & CAN_EFF_FLAG) != 0; }
/**
* Determines if this is a remote transmission request (RTR) frame.
* @return @em true if this is an RTR frame, @em false if not
*/
bool is_remote() const { return (can_id & CAN_RTR_FLAG) != 0; }
/**
* Determines if this is an error frame.
* @return @em true if this is an error frame, @em false if not
*/
bool is_error() const { return (can_id & CAN_ERR_FLAG) != 0; }
/**
* Gets the numeric CAN ID.
* @return The numeric CAN ID.
*/
canid_t id_value() const {
if (has_extended_id())
return can_id & CAN_EFF_MASK;
return can_id & CAN_SFF_MASK;
}
/**
* Sets the ID as a standard 11-bit value.
* @param canID The new CAN ID, in the range 0 - 0x7FF.
*/
void set_standard_id(canid_t canID) {
can_id &= ~(CAN_EFF_FLAG | CAN_SFF_MASK);
can_id |= canID & CAN_SFF_MASK;
}
/**
* Sets the ID as an extended 29-bit value.
* @param canID The new CAN ID, in the range 0 - 0x1FFFFFFF.
*/
void set_extended_id(canid_t canID) {
can_id &= ~CAN_EFF_MASK;
can_id |= CAN_EFF_FLAG | (canID & CAN_EFF_MASK);
}
};
/////////////////////////////////////////////////////////////////////////////
/**
* A remote transfer request (RTR) frame.
* This is a classic frame with no data and the RTR flag set.
*/
class can_remote_frame : public can_frame
{
/** The base class is the CAN frame */
using base = can_frame;
/** The size of the underlying address struct, in bytes */
static constexpr size_t SZ = sizeof(can_frame);
public:
/** Create a default remote frame */
can_remote_frame() : base{CAN_RTR_FLAG} {}
/**
* Create a remote frame for the specified ID.
* @param canID The CAN identifier for the frame
*/
can_remote_frame(canid_t canID) : base{CAN_RTR_FLAG | canID} {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Class that represents a Linux SocketCAN FD frame.
* This inherits from the Linux CAN fdframe struct, just providing easier
* construction.
*/
class canfd_frame : public ::canfd_frame
{
/** The base class is the C library CAN frame struct. */
using base = ::canfd_frame;
/** The size of the underlying address struct, in bytes */
static constexpr size_t SZ = sizeof(::canfd_frame);
public:
/**
* Constructs an empty frame.
* The frame is initialized to all zeroes.
*/
canfd_frame() : base{} {}
/**
* Constructs a frame with the specified ID and data.
* @param canID The CAN identifier for the frame
* @param data The data field for the frame
*/
canfd_frame(canid_t canID, const string& data)
: canfd_frame{canID, data.data(), data.length()} {}
/**
* Constructs a frame with the specified ID and data.
* @param canID The CAN identifier for the frame
* @param data The data field for the frame
* @param n The number of bytes in the data field
*/
canfd_frame(canid_t canID, const void* data, size_t n) : base{} {
this->can_id = canID;
if (data && n != 0) {
this->len = n;
::memcpy(&this->data, data, n);
}
}
/**
* Construct a frame from a C library CAN frame.
* @param frame A C lib CAN frame.
*/
canfd_frame(const base& frame) : base{frame} {}
/**
* Determines if this frame has an extended (29-bit) CAN ID.
* @return @em true if this frame has an extended ID, @em false if not
*/
bool has_extended_id() const { return (can_id & CAN_EFF_FLAG) != 0; }
/**
* Gets the numeric CAN ID.
* @return The numeric CAN ID.
*/
canid_t id_value() const {
if (has_extended_id())
return can_id & CAN_EFF_MASK;
return can_id & CAN_SFF_MASK;
}
/**
* Sets the ID as a standard 11-bit value.
* @param canID The new CAN ID, in the range 0 - 0x7FF.
*/
void set_standard_id(canid_t canID) {
can_id &= ~(CAN_EFF_FLAG | CAN_SFF_MASK);
can_id |= canID & CAN_SFF_MASK;
}
/**
* Sets the ID as an extended 29-bit value.
* @param canID The new CAN ID, in the range 0 - 0x1FFFFFFF.
*/
void set_extended_id(canid_t canID) {
can_id &= ~CAN_EFF_MASK;
can_id |= CAN_EFF_FLAG | (canID & CAN_EFF_MASK);
}
};
/////////////////////////////////////////////////////////////////////////////

View File

@@ -168,6 +168,16 @@ public:
* @return A floating-point timestamp with microsecond precision.
*/
double last_frame_timestamp();
/**
* Turn FD mode for the socket on or off.
*
* When enabled, it allows the socket to read or write the larger CAN FD
* frames.
*/
result<> set_fd_mode(bool on = true) {
int val = (on) ? 1 : 0;
return set_option(SOL_CAN_RAW, CAN_RAW_FD_FRAMES, val);
}
// ----- Filters -----
@@ -202,14 +212,15 @@ public:
return set_filters(filters.data(), filters.size());
}
// ----- I/O -----
// ----- I/O (classic) -----
/**
* Sends a frame to the CAN interface at the specified address.
* @param frame The CAN frame to send.
* @param flags The flags. See send(2).
* @param addr The remote destination of the data.
* @return the number of bytes sent on success or, @em -1 on failure.
* @return The number of bytes sent on success, or the error code on
* failure.
*/
result<size_t> send_to(const can_frame& frame, int flags, const can_address& addr) {
return base::send_to(&frame, sizeof(can_frame), flags, addr);
@@ -218,7 +229,8 @@ public:
* Sends a frame to the CAN interface at the specified address.
* @param frame The CAN frame to send.
* @param addr The remote destination of the data.
* @return the number of bytes sent on success or, @em -1 on failure.
* @return The number of bytes sent on success, or the error code on
* failure.
*/
result<size_t> send_to(const can_frame& frame, const can_address& addr) {
return send_to(frame, 0, addr);
@@ -228,7 +240,8 @@ public:
* The socket should be bound before calling this.
* @param frame The CAN frame to send.
* @param flags The option bit flags. See send(2).
* @return @em zero on success, @em -1 on failure.
* @return The number of bytes sent on success, or the error code on
* failure.
*/
result<size_t> send(const can_frame& frame, int flags = 0) {
return base::send(&frame, sizeof(can_frame), flags);
@@ -239,7 +252,8 @@ public:
* @param flags The option bit flags. See send(2).
* @param srcAddr Receives the address of the peer that sent the
* message
* @return The number of bytes read or @em -1 on error.
* @return The number of bytes read on success, or the error code on
* failure.
*/
result<size_t> recv_from(can_frame* frame, int flags, can_address* srcAddr = nullptr) {
return base::recv_from(frame, sizeof(frame), flags, srcAddr);
@@ -248,11 +262,70 @@ public:
* Receives a message on the socket.
* @param frame CAN frame to get the incoming data.
* @param flags The option bit flags. See send(2).
* @return The number of bytes read or @em -1 on error.
* @return The number of bytes read on success, or the error code on
* failure.
*/
result<size_t> recv(can_frame* frame, int flags = 0) {
return base::recv(frame, sizeof(can_frame), flags);
}
// ----- I/O (FD) -----
/**
* Sends an FD frame to the CAN interface at the specified address.
* @param frame The CAN FD frame to send.
* @param flags The flags. See send(2).
* @param addr The remote destination of the data.
* @return The number of bytes sent on success, or the error code on
* failure.
*/
result<size_t> send_to(const canfd_frame& frame, int flags, const can_address& addr) {
return base::send_to(&frame, sizeof(canfd_frame), flags, addr);
}
/**
* Sends an FD frame to the CAN interface at the specified address.
* @param frame The CAN FD frame to send.
* @param addr The remote destination of the data.
* @return The number of bytes sent on success, or the error code on
* failure.
*/
result<size_t> send_to(const canfd_frame& frame, const can_address& addr) {
return send_to(frame, 0, addr);
}
/**
* Sends an FD frame to the CAN bus.
* The socket should be bound before calling this.
* @param frame The CAN FD frame to send.
* @param flags The option bit flags. See send(2).
* @return The number of bytes sent on success, or the error code on
* failure.
*/
result<size_t> send(const canfd_frame& frame, int flags = 0) {
return base::send(&frame, sizeof(canfd_frame), flags);
}
/**
* Receives an FD frame from the CAN interface with the specified
* address.
* @param frame CAN frame to get the incoming data.
* @param flags The option bit flags. See send(2).
* @param srcAddr Receives the address of the peer that sent the
* message, if non-null.
* @return The number of bytes read on success, or the error code on
* failure.
*/
result<size_t> recv_from(canfd_frame* frame, int flags, can_address* srcAddr = nullptr) {
return base::recv_from(frame, sizeof(canfd_frame), flags, srcAddr);
}
/**
* Receives an FD frame on the socket.
* @param frame CAN FD frame to get the incoming data.
* @param flags The option bit flags. See send(2).
* @return The number of bytes read on success, or the error code on
* failure.
*/
result<size_t> recv(canfd_frame* frame, int flags = 0) {
return base::recv(frame, sizeof(canfd_frame), flags);
}
};
/////////////////////////////////////////////////////////////////////////////

View File

@@ -125,7 +125,7 @@ public:
* @param addr The other address
* @throw invalid_argument if the address is not IPv6
*/
inet6_address(const sock_address& addr);
explicit inet6_address(const sock_address& addr);
/**
* Constructs the address by copying the specified structure.
* @param addr The other address

View File

@@ -118,7 +118,7 @@ public:
* @param addr The other address
* @throw invalid_argument if the address is not IPv4
*/
inet_address(const sockaddr& addr);
explicit inet_address(const sockaddr& addr);
/**
* Constructs the address by copying the specified structure.
* @param addr The other address
@@ -130,7 +130,7 @@ public:
* @param addr The other address
* @throw invalid_argument if the address is not IPv4
*/
inet_address(const sock_address& addr);
explicit inet_address(const sock_address& addr);
/**
* Constructs the address by copying the specified structure.
* @param addr The other address

View File

@@ -620,24 +620,14 @@ public:
* @return The number of bytes sent on success or, the error code on
* failure.
*/
result<size_t> send_to(const void* buf, size_t n, int flags, const sock_address& addr) {
#if defined(_WIN32)
auto cbuf = reinterpret_cast<const char*>(buf);
return check_res<size_t>(
::sendto(handle(), cbuf, int(n), flags, addr.sockaddr_ptr(), addr.size())
);
#else
return check_res<ssize_t, size_t>(
::sendto(handle(), buf, n, flags, addr.sockaddr_ptr(), addr.size())
);
#endif
}
result<size_t> send_to(const void* buf, size_t n, int flags, const sock_address& addr);
/**
* Sends a message to another socket.
* @param buf The data to send.
* @param n The number of bytes in the data buffer.
* @param addr The remote destination of the data.
* @return the number of bytes sent on success or, @em -1 on failure.
* @return the number of bytes sent on success or, the error code on
* failure.
*/
result<size_t> send_to(const void* buf, size_t n, const sock_address& addr) {
return send_to(buf, n, 0, addr);
@@ -646,7 +636,8 @@ public:
* Sends a string to another socket.
* @param s The string to send.
* @param addr The remote destination of the data.
* @return the number of bytes sent on success or, @em -1 on failure.
* @return The number of bytes sent on success or, the error code on
* failure.
*/
result<size_t> send_to(const string& s, const sock_address& addr) {
return send_to(s.data(), s.length(), 0, addr);
@@ -657,23 +648,17 @@ public:
* @param buf The date to send.
* @param n The number of bytes in the data buffer.
* @param flags The option bit flags. See send(2).
* @return @em zero on success, @em -1 on failure.
* @return The number of bytes sent on success or, the error code on
* failure.
*/
result<size_t> send(const void* buf, size_t n, int flags = 0) {
#if defined(_WIN32)
return check_res<ssize_t, size_t>(
::send(handle(), reinterpret_cast<const char*>(buf), int(n), flags)
);
#else
return check_res<ssize_t, size_t>(::send(handle(), buf, n, flags));
#endif
}
result<size_t> send(const void* buf, size_t n, int flags = 0);
/**
* Sends a string to the socket at the default address.
* The socket should be connected before calling this
* @param s The string to send.
* @param flags The option bit flags. See send(2).
* @return @em zero on success, @em -1 on failure.
* @return The number of bytes sent on success, or the error code on
* failure.
*/
result<size_t> send(const string& s, int flags = 0) {
return send(s.data(), s.length(), flags);
@@ -685,7 +670,8 @@ public:
* @param flags The option bit flags. See send(2).
* @param srcAddr Receives the address of the peer that sent the
* message
* @return The number of bytes read or @em -1 on error.
* @return The number of bytes read on success, or the error code on
* failure.
*/
result<size_t> recv_from(void* buf, size_t n, int flags, sock_address* srcAddr = nullptr);
/**
@@ -694,7 +680,8 @@ public:
* @param n The number of bytes to read.
* @param srcAddr Receives the address of the peer that sent the
* message
* @return The number of bytes read or @em -1 on error.
* @return The number of bytes read on success, or the error code on
* failure.
*/
result<size_t> recv_from(void* buf, size_t n, sock_address* srcAddr = nullptr) {
return recv_from(buf, n, 0, srcAddr);
@@ -704,17 +691,10 @@ public:
* @param buf Buffer to get the incoming data.
* @param n The number of bytes to read.
* @param flags The option bit flags. See send(2).
* @return The number of bytes read or @em -1 on error.
* @return The number of bytes read on success, or the error code on
* failure.
*/
result<size_t> recv(void* buf, size_t n, int flags = 0) {
#if defined(_WIN32)
return check_res<ssize_t, size_t>(
::recv(handle(), reinterpret_cast<char*>(buf), int(n), flags)
);
#else
return check_res<ssize_t, size_t>(::recv(handle(), buf, n, flags));
#endif
}
result<size_t> recv(void* buf, size_t n, int flags = 0);
};
/////////////////////////////////////////////////////////////////////////////

View File

@@ -94,7 +94,7 @@ public:
* @param path The path to the socket file.
* @throw system_error if the path is invalid (too long, etc)
*/
unix_address(const string& path);
explicit unix_address(const string& path);
/**
* Constructs an address given the specified path.
* Sets the error code on failure.
@@ -106,7 +106,7 @@ public:
* @param addr The other address
* @throw invalid_argument if the address is not IPv4
*/
unix_address(const sockaddr& addr);
explicit unix_address(const sockaddr& addr);
/**
* Constructs the address by copying the specified structure.
* @param addr The other address
@@ -118,7 +118,7 @@ public:
* @param addr The other address
* @throw invalid_argument if the address is not IPv4
*/
unix_address(const sock_address& addr);
explicit unix_address(const sock_address& addr);
/**
* Constructs the address by copying the specified structure.
* @param addr The other address

View File

@@ -327,6 +327,32 @@ result<> socket::close() {
}
// --------------------------------------------------------------------------
// I/O
result<size_t> socket::send_to(
const void* buf, size_t n, int flags, const sock_address& addr
) {
#if defined(_WIN32)
auto cbuf = reinterpret_cast<const char*>(buf);
return check_res<size_t>(
::sendto(handle(), cbuf, int(n), flags, addr.sockaddr_ptr(), addr.size())
);
#else
return check_res<ssize_t, size_t>(
::sendto(handle(), buf, n, flags, addr.sockaddr_ptr(), addr.size())
);
#endif
}
result<size_t> socket::send(const void* buf, size_t n, int flags /*=0*/) {
#if defined(_WIN32)
return check_res<ssize_t, size_t>(
::send(handle(), reinterpret_cast<const char*>(buf), int(n), flags)
);
#else
return check_res<ssize_t, size_t>(::send(handle(), buf, n, flags));
#endif
}
result<size_t>
socket::recv_from(void* buf, size_t n, int flags, sock_address* srcAddr /*=nullptr*/) {
@@ -344,5 +370,15 @@ socket::recv_from(void* buf, size_t n, int flags, sock_address* srcAddr /*=nullp
#endif
}
result<size_t> socket::recv(void* buf, size_t n, int flags /*=0*/) {
#if defined(_WIN32)
return check_res<ssize_t, size_t>(
::recv(handle(), reinterpret_cast<char*>(buf), int(n), flags)
);
#else
return check_res<ssize_t, size_t>(::recv(handle(), buf, n, flags));
#endif
}
/////////////////////////////////////////////////////////////////////////////
} // namespace sockpp