2#include <ossia/network/sockets/configuration.hpp>
5#include <ossia/network/sockets/writers.hpp>
7#include <boost/asio/buffer.hpp>
8#include <boost/asio/error.hpp>
9#include <boost/asio/io_context.hpp>
10#include <boost/asio/posix/stream_descriptor.hpp>
11#include <boost/asio/post.hpp>
14#include <linux/can/error.h>
15#include <linux/can/raw.h>
18#include <sys/socket.h>
20#include <nano_signal_slot.hpp>
24#include <system_error>
59 bool bitrate_switch{};
65 uint8_t data[CANFD_MAX_DLEN]{};
82 using socket = boost::asio::posix::stream_descriptor;
84 can_socket(
const can_configuration& conf, boost::asio::io_context& ctx)
91 can_socket(
const can_socket&) =
delete;
92 can_socket(can_socket&&) =
delete;
93 can_socket& operator=(
const can_socket&) =
delete;
94 can_socket& operator=(can_socket&&) =
delete;
109 int fd = ::socket(PF_CAN, SOCK_RAW, CAN_RAW);
111 throw_errno(
"socket(PF_CAN, SOCK_RAW, CAN_RAW)");
118 if(m_conf.interface_name.size() >=
sizeof(ifr.ifr_name))
119 throw std::system_error{
120 std::make_error_code(std::errc::filename_too_long),
121 "CAN interface name too long: " + m_conf.interface_name};
123 ifr.ifr_name, m_conf.interface_name.c_str(),
sizeof(ifr.ifr_name) - 1);
124 if(::ioctl(fd, SIOCGIFINDEX, &ifr) < 0)
125 throw_errno(
"no such CAN interface: " + m_conf.interface_name);
130 addr.can_family = AF_CAN;
131 addr.can_ifindex = ifr.ifr_ifindex;
132 if(::bind(fd,
reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) < 0)
133 throw_errno(
"bind(" + m_conf.interface_name +
")");
149 boost::asio::post(m_context, [
this, alive = watch()] {
158 if(m_socket.is_open())
161 boost::asio::post(m_context, [
this, alive = watch()] {
170 bool connected() const noexcept {
return m_socket.is_open(); }
175 boost::system::error_code write(
const can_message& msg)
177 boost::system::error_code ec;
178 if(!m_socket.is_open())
179 return boost::asio::error::not_connected;
188 f.can_id = raw_id(msg);
189 f.len = msg.size > CANFD_MAX_DLEN ? CANFD_MAX_DLEN : msg.size;
190 if(msg.bitrate_switch)
191 f.flags |= CANFD_BRS;
193 f.flags |= CANFD_ESI;
194 std::memcpy(f.data, msg.data, f.len);
195 m_socket.write_some(boost::asio::buffer(&f,
sizeof(f)), ec);
200 f.can_id = raw_id(msg);
201 f.can_dlc = msg.size > CAN_MAX_DLEN ? CAN_MAX_DLEN : msg.size;
203 std::memcpy(f.data, msg.data, f.can_dlc);
204 m_socket.write_some(boost::asio::buffer(&f,
sizeof(f)), ec);
213 template <
typename F>
216 struct proc : stream_processor<can_socket, F>
219 do_receive(proc{*
this, std::move(f)});
222 Nano::Signal<void()> on_open;
223 Nano::Signal<void()> on_close;
224 Nano::Signal<void()> on_fail;
227 Nano::Signal<void(boost::system::error_code)> on_write_error;
229 boost::asio::io_context& m_context;
230 can_configuration m_conf;
237 lifetime_token m_lifetime;
238 std::weak_ptr<void> watch() const noexcept {
return m_lifetime.watch(); }
245 static constexpr std::size_t max_frame_size = 2060;
246 alignas(64) uint8_t m_readbuf[max_frame_size];
248 [[noreturn]]
static void throw_errno(
const std::string& what)
250 throw std::system_error{errno, std::generic_category(), what};
253 static uint32_t raw_id(
const can_message& msg)
noexcept
255 uint32_t
id = msg.extended ? (msg.id & CAN_EFF_MASK) | CAN_EFF_FLAG
256 : (msg.id & CAN_SFF_MASK);
259 if(msg.remote && !msg.fd)
264 void apply_options(
int fd)
273 if(::setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &on,
sizeof(on)) < 0)
274 throw_errno(
"this kernel or interface does not support CAN FD");
277 if(!m_conf.filters.empty())
281 std::vector<can_filter> filters;
282 filters.reserve(m_conf.filters.size());
283 for(
auto& f : m_conf.filters)
284 filters.push_back(can_filter{.can_id = f.id, .can_mask = f.mask});
287 fd, SOL_CAN_RAW, CAN_RAW_FILTER, filters.data(),
288 filters.size() *
sizeof(can_filter))
290 throw_errno(
"could not apply the CAN filters");
294 const can_err_mask_t mask = m_conf.error_frames ? CAN_ERR_MASK : 0;
295 if(::setsockopt(fd, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &mask,
sizeof(mask)) < 0)
296 throw_errno(
"could not set the CAN error filter");
300 const int loopback = m_conf.loopback ? 1 : 0;
301 if(::setsockopt(fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback,
sizeof(loopback))
303 throw_errno(
"could not set CAN_RAW_LOOPBACK");
307 const int own = m_conf.receive_own_messages ? 1 : 0;
308 if(::setsockopt(fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &own,
sizeof(own)) < 0)
309 throw_errno(
"could not set CAN_RAW_RECV_OWN_MSGS");
313 void close_impl() noexcept
315 if(m_socket.is_open())
317 boost::system::error_code ec;
322 template <
typename F>
325 m_socket.async_read_some(
326 boost::asio::buffer(m_readbuf),
327 [
this, alive = watch(),
328 f = std::move(f)](boost::system::error_code ec, std::size_t sz)
mutable {
332 if(!f.validate_stream(ec))
345 if(can_message msg; decode(m_readbuf, sz, msg))
348 do_receive(std::move(f));
358 static bool decode(
const uint8_t* data, std::size_t sz, can_message& msg)
noexcept
364 std::memcpy(&f, data,
sizeof(f));
367 msg.remote = (raw & CAN_RTR_FLAG) != 0;
368 msg.size = f.can_dlc > CAN_MAX_DLEN ? CAN_MAX_DLEN : f.can_dlc;
370 std::memcpy(msg.data, f.data, msg.size);
372 else if(sz == CANFD_MTU)
375 std::memcpy(&f, data,
sizeof(f));
379 msg.bitrate_switch = (f.flags & CANFD_BRS) != 0;
380 msg.error_state = (f.flags & CANFD_ESI) != 0;
381 msg.size = f.len > CANFD_MAX_DLEN ? CANFD_MAX_DLEN : f.len;
382 std::memcpy(msg.data, f.data, msg.size);
392 msg.error = (raw & CAN_ERR_FLAG) != 0;
397 msg.extended =
false;
398 msg.id = raw & CAN_ERR_MASK;
402 msg.extended = (raw & CAN_EFF_FLAG) != 0;
403 msg.id = msg.extended ? (raw & CAN_EFF_MASK) : (raw & CAN_SFF_MASK);