123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 |
- /*
- * Copyright (C) 2014-2015 Pavel Kirienko <pavel.kirienko@gmail.com>
- * Ilia Sheremet <illia.sheremet@gmail.com>
- */
- #pragma once
- #include <cassert>
- #include <cstdint>
- #include <queue>
- #include <vector>
- #include <map>
- #include <unordered_set>
- #include <memory>
- #include <algorithm>
- #include <fcntl.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include <net/if.h>
- #include <linux/can.h>
- #include <linux/can/raw.h>
- #include <poll.h>
- #include <uavcan/uavcan.hpp>
- #include <uavcan_linux/clock.hpp>
- #include <uavcan_linux/exception.hpp>
- namespace uavcan_linux
- {
- /**
- * SocketCan driver class keeps number of each kind of errors occurred since the object was created.
- */
- enum class SocketCanError
- {
- SocketReadFailure,
- SocketWriteFailure,
- TxTimeout
- };
- /**
- * Single SocketCAN socket interface.
- *
- * SocketCAN socket adapter maintains TX and RX queues in user space. At any moment socket's buffer contains
- * no more than 'max_frames_in_socket_tx_queue_' TX frames, rest is waiting in the user space TX queue; when the
- * socket produces loopback for the previously sent TX frame the next frame from the user space TX queue will
- * be sent into the socket.
- *
- * This approach allows to properly maintain TX timeouts (http://stackoverflow.com/questions/19633015/).
- * TX timestamping is implemented by means of reading RX timestamps of loopback frames (see "TX timestamping" on
- * linux-can mailing list, http://permalink.gmane.org/gmane.linux.can/5322).
- *
- * Note that if max_frames_in_socket_tx_queue_ is greater than one, frame reordering may occur (depending on the
- * unrderlying logic).
- *
- * This class is too complex and needs to be refactored later. At least, basic socket IO and configuration
- * should be extracted into a different class.
- */
- class SocketCanIface : public uavcan::ICanIface
- {
- static inline ::can_frame makeSocketCanFrame(const uavcan::CanFrame& uavcan_frame)
- {
- ::can_frame sockcan_frame { uavcan_frame.id& uavcan::CanFrame::MaskExtID, uavcan_frame.dlc, { } };
- (void)std::copy(uavcan_frame.data, uavcan_frame.data + uavcan_frame.dlc, sockcan_frame.data);
- if (uavcan_frame.isExtended())
- {
- sockcan_frame.can_id |= CAN_EFF_FLAG;
- }
- if (uavcan_frame.isErrorFrame())
- {
- sockcan_frame.can_id |= CAN_ERR_FLAG;
- }
- if (uavcan_frame.isRemoteTransmissionRequest())
- {
- sockcan_frame.can_id |= CAN_RTR_FLAG;
- }
- return sockcan_frame;
- }
- static inline uavcan::CanFrame makeUavcanFrame(const ::can_frame& sockcan_frame)
- {
- uavcan::CanFrame uavcan_frame(sockcan_frame.can_id & CAN_EFF_MASK, sockcan_frame.data, sockcan_frame.can_dlc);
- if (sockcan_frame.can_id & CAN_EFF_FLAG)
- {
- uavcan_frame.id |= uavcan::CanFrame::FlagEFF;
- }
- if (sockcan_frame.can_id & CAN_ERR_FLAG)
- {
- uavcan_frame.id |= uavcan::CanFrame::FlagERR;
- }
- if (sockcan_frame.can_id & CAN_RTR_FLAG)
- {
- uavcan_frame.id |= uavcan::CanFrame::FlagRTR;
- }
- return uavcan_frame;
- }
- struct TxItem
- {
- uavcan::CanFrame frame;
- uavcan::MonotonicTime deadline;
- uavcan::CanIOFlags flags = 0;
- std::uint64_t order = 0;
- TxItem(const uavcan::CanFrame& arg_frame, uavcan::MonotonicTime arg_deadline,
- uavcan::CanIOFlags arg_flags, std::uint64_t arg_order)
- : frame(arg_frame)
- , deadline(arg_deadline)
- , flags(arg_flags)
- , order(arg_order)
- { }
- bool operator<(const TxItem& rhs) const
- {
- if (frame.priorityLowerThan(rhs.frame))
- {
- return true;
- }
- if (frame.priorityHigherThan(rhs.frame))
- {
- return false;
- }
- return order > rhs.order;
- }
- };
- struct RxItem
- {
- uavcan::CanFrame frame;
- uavcan::MonotonicTime ts_mono;
- uavcan::UtcTime ts_utc;
- uavcan::CanIOFlags flags;
- RxItem()
- : flags(0)
- { }
- };
- const SystemClock& clock_;
- const int fd_;
- const unsigned max_frames_in_socket_tx_queue_;
- unsigned frames_in_socket_tx_queue_ = 0;
- std::uint64_t tx_frame_counter_ = 0; ///< Increments with every frame pushed into the TX queue
- std::map<SocketCanError, std::uint64_t> errors_;
- std::priority_queue<TxItem> tx_queue_; // TODO: Use pool allocator
- std::queue<RxItem> rx_queue_; // TODO: Use pool allocator
- std::unordered_multiset<std::uint32_t> pending_loopback_ids_; // TODO: Use pool allocator
- std::vector<::can_filter> hw_filters_container_;
- void registerError(SocketCanError e) { errors_[e]++; }
- void incrementNumFramesInSocketTxQueue()
- {
- assert(frames_in_socket_tx_queue_ < max_frames_in_socket_tx_queue_);
- frames_in_socket_tx_queue_++;
- }
- void confirmSentFrame()
- {
- if (frames_in_socket_tx_queue_ > 0)
- {
- frames_in_socket_tx_queue_--;
- }
- else
- {
- assert(0); // Loopback for a frame that we didn't send.
- }
- }
- bool wasInPendingLoopbackSet(const uavcan::CanFrame& frame)
- {
- if (pending_loopback_ids_.count(frame.id) > 0)
- {
- (void)pending_loopback_ids_.erase(frame.id);
- return true;
- }
- return false;
- }
- int write(const uavcan::CanFrame& frame) const
- {
- errno = 0;
- const ::can_frame sockcan_frame = makeSocketCanFrame(frame);
- const int res = ::write(fd_, &sockcan_frame, sizeof(sockcan_frame));
- if (res <= 0)
- {
- if (errno == ENOBUFS || errno == EAGAIN) // Writing is not possible atm, not an error
- {
- return 0;
- }
- return res;
- }
- if (res != sizeof(sockcan_frame))
- {
- return -1;
- }
- return 1;
- }
- /**
- * SocketCAN git show 1e55659ce6ddb5247cee0b1f720d77a799902b85
- * MSG_DONTROUTE is set for any packet from localhost,
- * MSG_CONFIRM is set for any pakcet of your socket.
- * Diff: https://git.ucsd.edu/abuss/linux/commit/1e55659ce6ddb5247cee0b1f720d77a799902b85
- * Man: https://www.kernel.org/doc/Documentation/networking/can.txt (chapter 4.1.6).
- */
- int read(uavcan::CanFrame& frame, uavcan::UtcTime& ts_utc, bool& loopback) const
- {
- auto iov = ::iovec();
- auto sockcan_frame = ::can_frame();
- iov.iov_base = &sockcan_frame;
- iov.iov_len = sizeof(sockcan_frame);
- struct Control
- {
- cmsghdr cm;
- std::uint8_t data[sizeof(::timeval)];
- };
- auto control = Control();
- auto msg = ::msghdr();
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = &control;
- msg.msg_controllen = sizeof(control);
- const int res = ::recvmsg(fd_, &msg, MSG_DONTWAIT);
- if (res <= 0)
- {
- return (res < 0 && errno == EWOULDBLOCK) ? 0 : res;
- }
- /*
- * Flags
- */
- loopback = (msg.msg_flags & static_cast<int>(MSG_CONFIRM)) != 0;
- if (!loopback && !checkHWFilters(sockcan_frame))
- {
- return 0;
- }
- frame = makeUavcanFrame(sockcan_frame);
- /*
- * Timestamp
- */
- const ::cmsghdr* const cmsg = CMSG_FIRSTHDR(&msg);
- assert(cmsg != nullptr);
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP)
- {
- auto tv = ::timeval();
- (void)std::memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv)); // Copy to avoid alignment problems
- assert(tv.tv_sec >= 0 && tv.tv_usec >= 0);
- ts_utc = uavcan::UtcTime::fromUSec(std::uint64_t(tv.tv_sec) * 1000000ULL + tv.tv_usec);
- }
- else
- {
- assert(0);
- return -1;
- }
- return 1;
- }
- void pollWrite()
- {
- while (hasReadyTx())
- {
- const TxItem tx = tx_queue_.top();
- if (tx.deadline >= clock_.getMonotonic())
- {
- const int res = write(tx.frame);
- if (res == 1) // Transmitted successfully
- {
- incrementNumFramesInSocketTxQueue();
- if (tx.flags & uavcan::CanIOFlagLoopback)
- {
- (void)pending_loopback_ids_.insert(tx.frame.id);
- }
- }
- else if (res == 0) // Not transmitted, nor is it an error
- {
- break; // Leaving the loop, the frame remains enqueued for the next retry
- }
- else // Transmission error
- {
- registerError(SocketCanError::SocketWriteFailure);
- }
- }
- else
- {
- registerError(SocketCanError::TxTimeout);
- }
- // Removing the frame from the queue even if transmission failed
- tx_queue_.pop();
- }
- }
- void pollRead()
- {
- while (true)
- {
- RxItem rx;
- rx.ts_mono = clock_.getMonotonic(); // Monotonic timestamp is not required to be precise (unlike UTC)
- bool loopback = false;
- const int res = read(rx.frame, rx.ts_utc, loopback);
- if (res == 1)
- {
- assert(!rx.ts_utc.isZero());
- bool accept = true;
- if (loopback) // We receive loopback for all CAN frames
- {
- confirmSentFrame();
- rx.flags |= uavcan::CanIOFlagLoopback;
- accept = wasInPendingLoopbackSet(rx.frame); // Do we need to send this loopback into the lib?
- }
- if (accept)
- {
- rx.ts_utc += clock_.getPrivateAdjustment();
- rx_queue_.push(rx);
- }
- }
- else if (res == 0)
- {
- break;
- }
- else
- {
- registerError(SocketCanError::SocketReadFailure);
- break;
- }
- }
- }
- /**
- * Returns true if a frame accepted by HW filters
- */
- bool checkHWFilters(const ::can_frame& frame) const
- {
- if (!hw_filters_container_.empty())
- {
- for (auto& f : hw_filters_container_)
- {
- if (((frame.can_id & f.can_mask) ^ f.can_id) == 0)
- {
- return true;
- }
- }
- return false;
- }
- else
- {
- return true;
- }
- }
- public:
- /**
- * Takes ownership of socket's file descriptor.
- *
- * @ref max_frames_in_socket_tx_queue See a note in the class comment.
- */
- SocketCanIface(const SystemClock& clock, int socket_fd, int max_frames_in_socket_tx_queue = 2)
- : clock_(clock)
- , fd_(socket_fd)
- , max_frames_in_socket_tx_queue_(max_frames_in_socket_tx_queue)
- {
- assert(fd_ >= 0);
- }
- /**
- * Socket file descriptor will be closed.
- */
- virtual ~SocketCanIface()
- {
- UAVCAN_TRACE("SocketCAN", "SocketCanIface: Closing fd %d", fd_);
- (void)::close(fd_);
- }
- /**
- * Assumes that the socket is writeable
- */
- std::int16_t send(const uavcan::CanFrame& frame, const uavcan::MonotonicTime tx_deadline,
- const uavcan::CanIOFlags flags) override
- {
- tx_queue_.emplace(frame, tx_deadline, flags, tx_frame_counter_);
- tx_frame_counter_++;
- pollRead(); // Read poll is necessary because it can release the pending TX flag
- pollWrite();
- return 1;
- }
- /**
- * Will read the socket only if RX queue is empty.
- * Normally, poll() needs to be executed first.
- */
- std::int16_t receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic,
- uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags) override
- {
- if (rx_queue_.empty())
- {
- pollRead(); // This allows to use the socket not calling poll() explicitly.
- if (rx_queue_.empty())
- {
- return 0;
- }
- }
- {
- const RxItem& rx = rx_queue_.front();
- out_frame = rx.frame;
- out_ts_monotonic = rx.ts_mono;
- out_ts_utc = rx.ts_utc;
- out_flags = rx.flags;
- }
- rx_queue_.pop();
- return 1;
- }
- /**
- * Performs socket read/write.
- * @param read Socket is readable
- * @param write Socket is writeable
- */
- void poll(bool read, bool write)
- {
- if (read)
- {
- pollRead(); // Read poll must be executed first because it may decrement frames_in_socket_tx_queue_
- }
- if (write)
- {
- pollWrite();
- }
- }
- bool hasReadyRx() const { return !rx_queue_.empty(); }
- bool hasReadyTx() const
- {
- return !tx_queue_.empty() && (frames_in_socket_tx_queue_ < max_frames_in_socket_tx_queue_);
- }
- std::int16_t configureFilters(const uavcan::CanFilterConfig* const filter_configs,
- const std::uint16_t num_configs) override
- {
- if (filter_configs == nullptr)
- {
- assert(0);
- return -1;
- }
- hw_filters_container_.clear();
- hw_filters_container_.resize(num_configs);
- for (unsigned i = 0; i < num_configs; i++)
- {
- const uavcan::CanFilterConfig& fc = filter_configs[i];
- hw_filters_container_[i].can_id = fc.id & uavcan::CanFrame::MaskExtID;
- hw_filters_container_[i].can_mask = fc.mask & uavcan::CanFrame::MaskExtID;
- if (fc.id & uavcan::CanFrame::FlagEFF)
- {
- hw_filters_container_[i].can_id |= CAN_EFF_FLAG;
- }
- if (fc.id & uavcan::CanFrame::FlagRTR)
- {
- hw_filters_container_[i].can_id |= CAN_RTR_FLAG;
- }
- if (fc.mask & uavcan::CanFrame::FlagEFF)
- {
- hw_filters_container_[i].can_mask |= CAN_EFF_FLAG;
- }
- if (fc.mask & uavcan::CanFrame::FlagRTR)
- {
- hw_filters_container_[i].can_mask |= CAN_RTR_FLAG;
- }
- }
- return 0;
- }
- /**
- * SocketCAN emulates the CAN filters in software, so the number of filters is virtually unlimited.
- * This method returns a constant value.
- */
- static constexpr unsigned NumFilters = 8;
- std::uint16_t getNumFilters() const override { return NumFilters; }
- /**
- * Returns total number of errors of each kind detected since the object was created.
- */
- std::uint64_t getErrorCount() const override
- {
- std::uint64_t ec = 0;
- for (auto& kv : errors_) { ec += kv.second; }
- return ec;
- }
- /**
- * Returns number of errors of each kind in a map.
- */
- const decltype(errors_) & getErrors() const { return errors_; }
- int getFileDescriptor() const { return fd_; }
- /**
- * Open and configure a CAN socket on iface specified by name.
- * @param iface_name String containing iface name, e.g. "can0", "vcan1", "slcan0"
- * @return Socket descriptor or negative number on error.
- */
- static int openSocket(const std::string& iface_name)
- {
- errno = 0;
- const int s = ::socket(PF_CAN, SOCK_RAW, CAN_RAW);
- if (s < 0)
- {
- return s;
- }
- class RaiiCloser
- {
- int fd_;
- public:
- RaiiCloser(int filedesc) : fd_(filedesc)
- {
- assert(fd_ >= 0);
- }
- ~RaiiCloser()
- {
- if (fd_ >= 0)
- {
- UAVCAN_TRACE("SocketCAN", "RaiiCloser: Closing fd %d", fd_);
- (void)::close(fd_);
- }
- }
- void disarm() { fd_ = -1; }
- } raii_closer(s);
- // Detect the iface index
- auto ifr = ::ifreq();
- if (iface_name.length() >= IFNAMSIZ)
- {
- errno = ENAMETOOLONG;
- return -1;
- }
- (void)std::strncpy(ifr.ifr_name, iface_name.c_str(), iface_name.length());
- if (::ioctl(s, SIOCGIFINDEX, &ifr) < 0 || ifr.ifr_ifindex < 0)
- {
- return -1;
- }
- // Bind to the specified CAN iface
- {
- auto addr = ::sockaddr_can();
- addr.can_family = AF_CAN;
- addr.can_ifindex = ifr.ifr_ifindex;
- if (::bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0)
- {
- return -1;
- }
- }
- // Configure
- {
- const int on = 1;
- // Timestamping
- if (::setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0)
- {
- return -1;
- }
- // Socket loopback
- if (::setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &on, sizeof(on)) < 0)
- {
- return -1;
- }
- // Non-blocking
- if (::fcntl(s, F_SETFL, O_NONBLOCK) < 0)
- {
- return -1;
- }
- }
- // Validate the resulting socket
- {
- int socket_error = 0;
- ::socklen_t errlen = sizeof(socket_error);
- (void)::getsockopt(s, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&socket_error), &errlen);
- if (socket_error != 0)
- {
- errno = socket_error;
- return -1;
- }
- }
- raii_closer.disarm();
- return s;
- }
- };
- /**
- * Multiplexing container for multiple SocketCAN sockets.
- * Uses ppoll() for multiplexing.
- *
- * When an interface becomes down/disconnected while the node is running,
- * the driver will silently exclude it from the IO loop and continue to run on the remaining interfaces.
- * When all interfaces become down/disconnected, the driver will throw @ref AllIfacesDownException
- * from @ref SocketCanDriver::select().
- * Whether a certain interface is down can be checked with @ref SocketCanDriver::isIfaceDown().
- */
- class SocketCanDriver : public uavcan::ICanDriver
- {
- class IfaceWrapper : public SocketCanIface
- {
- bool down_ = false;
- public:
- IfaceWrapper(const SystemClock& clock, int fd) : SocketCanIface(clock, fd) { }
- void updateDownStatusFromPollResult(const ::pollfd& pfd)
- {
- assert(pfd.fd == this->getFileDescriptor());
- if (!down_ && (pfd.revents & POLLERR))
- {
- int error = 0;
- ::socklen_t errlen = sizeof(error);
- (void)::getsockopt(pfd.fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&error), &errlen);
- down_ = error == ENETDOWN || error == ENODEV;
- UAVCAN_TRACE("SocketCAN", "Iface %d is dead; error %d", this->getFileDescriptor(), error);
- }
- }
- bool isDown() const { return down_; }
- };
- const SystemClock& clock_;
- std::vector<std::unique_ptr<IfaceWrapper>> ifaces_;
- public:
- /**
- * Reference to the clock object shall remain valid.
- */
- explicit SocketCanDriver(const SystemClock& clock)
- : clock_(clock)
- {
- ifaces_.reserve(uavcan::MaxCanIfaces);
- }
- /**
- * This function may return before deadline expiration even if no requested IO operations become possible.
- * This behavior makes implementation way simpler, and it is OK since libuavcan can properly handle such
- * early returns.
- * Also it can return more events than were originally requested by uavcan, which is also acceptable.
- */
- std::int16_t select(uavcan::CanSelectMasks& inout_masks,
- const uavcan::CanFrame* (&)[uavcan::MaxCanIfaces],
- uavcan::MonotonicTime blocking_deadline) override
- {
- // Detecting whether we need to block at all
- bool need_block = (inout_masks.write == 0); // Write queue is infinite
- for (unsigned i = 0; need_block && (i < ifaces_.size()); i++)
- {
- const bool need_read = inout_masks.read & (1 << i);
- if (need_read && ifaces_[i]->hasReadyRx())
- {
- need_block = false;
- }
- }
- if (need_block)
- {
- // Poll FD set setup
- ::pollfd pollfds[uavcan::MaxCanIfaces] = {};
- unsigned num_pollfds = 0;
- IfaceWrapper* pollfd_index_to_iface[uavcan::MaxCanIfaces] = { };
- for (unsigned i = 0; i < ifaces_.size(); i++)
- {
- if (!ifaces_[i]->isDown())
- {
- pollfds[num_pollfds].fd = ifaces_[i]->getFileDescriptor();
- pollfds[num_pollfds].events = POLLIN;
- if (ifaces_[i]->hasReadyTx() || (inout_masks.write & (1U << i)))
- {
- pollfds[num_pollfds].events |= POLLOUT;
- }
- pollfd_index_to_iface[num_pollfds] = ifaces_[i].get();
- num_pollfds++;
- }
- }
- // This is where we abort when the last iface goes down
- if (num_pollfds == 0)
- {
- throw AllIfacesDownException();
- }
- // Timeout conversion
- const std::int64_t timeout_usec = (blocking_deadline - clock_.getMonotonic()).toUSec();
- auto ts = ::timespec();
- if (timeout_usec > 0)
- {
- ts.tv_sec = timeout_usec / 1000000LL;
- ts.tv_nsec = (timeout_usec % 1000000LL) * 1000;
- }
- // Blocking here
- const int res = ::ppoll(pollfds, num_pollfds, &ts, nullptr);
- if (res < 0)
- {
- return res;
- }
- // Handling poll output
- for (unsigned i = 0; i < num_pollfds; i++)
- {
- pollfd_index_to_iface[i]->updateDownStatusFromPollResult(pollfds[i]);
- const bool poll_read = pollfds[i].revents & POLLIN;
- const bool poll_write = pollfds[i].revents & POLLOUT;
- pollfd_index_to_iface[i]->poll(poll_read, poll_write);
- }
- }
- // Writing the output masks
- inout_masks = uavcan::CanSelectMasks();
- for (unsigned i = 0; i < ifaces_.size(); i++)
- {
- if (!ifaces_[i]->isDown())
- {
- inout_masks.write |= std::uint8_t(1U << i); // Always ready to write if not down
- }
- if (ifaces_[i]->hasReadyRx())
- {
- inout_masks.read |= std::uint8_t(1U << i); // Readability depends only on RX buf, even if down
- }
- }
- // Return value is irrelevant as long as it's non-negative
- return ifaces_.size();
- }
- SocketCanIface* getIface(std::uint8_t iface_index) override
- {
- return (iface_index >= ifaces_.size()) ? nullptr : ifaces_[iface_index].get();
- }
- std::uint8_t getNumIfaces() const override { return ifaces_.size(); }
- /**
- * Adds one iface by name. Will fail if there are @ref MaxIfaces ifaces registered already.
- * @param iface_name E.g. "can0", "vcan1"
- * @return Negative on error, interface index on success.
- * @throws uavcan_linux::Exception.
- */
- int addIface(const std::string& iface_name)
- {
- if (ifaces_.size() >= uavcan::MaxCanIfaces)
- {
- return -1;
- }
- // Open the socket
- const int fd = SocketCanIface::openSocket(iface_name);
- if (fd < 0)
- {
- return fd;
- }
- // Construct the iface - upon successful construction the iface will take ownership of the fd.
- try
- {
- ifaces_.emplace_back(new IfaceWrapper(clock_, fd));
- }
- catch (...)
- {
- (void)::close(fd);
- throw;
- }
- UAVCAN_TRACE("SocketCAN", "New iface '%s' fd %d", iface_name.c_str(), fd);
- return ifaces_.size() - 1;
- }
- /**
- * Returns false if the specified interface is functioning, true if it became unavailable.
- */
- bool isIfaceDown(std::uint8_t iface_index) const
- {
- return ifaces_.at(iface_index)->isDown();
- }
- };
- }
|