LCOV - code coverage report
Current view: top level - AP_HAL_Linux - CANSocketIface.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 261 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 25 0.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             : */
      15             : 
      16             : /*
      17             :  * Many thanks to members of the UAVCAN project:
      18             :  *  Pavel Kirienko <[email protected]>
      19             :  *  Ilia Sheremet <[email protected]>
      20             :  *
      21             :  *  license info can be found in the uavcan submodule located:
      22             :  *  modules/uavcan/libuavcan_drivers/linux/include/uavcan_linux/socketcan.hpp
      23             :  */
      24             : 
      25             : #include <AP_HAL/AP_HAL.h>
      26             : #include <AP_HAL/system.h>
      27             : 
      28             : #if HAL_NUM_CAN_IFACES
      29             : 
      30             : #include "CANSocketIface.h"
      31             : 
      32             : #include <unistd.h>
      33             : #include <fcntl.h>
      34             : 
      35             : #include <sys/socket.h>
      36             : #include <sys/ioctl.h>
      37             : #include <sys/time.h>
      38             : #include <net/if.h>
      39             : #include <linux/can/raw.h>
      40             : #include <cstring>
      41             : #include "Scheduler.h"
      42             : #include <AP_CANManager/AP_CANManager.h>
      43             : #include <AP_Common/ExpandingString.h>
      44             : 
      45             : extern const AP_HAL::HAL& hal;
      46             : 
      47             : using namespace Linux;
      48             : 
      49           0 : static can_frame makeSocketCanFrame(const AP_HAL::CANFrame& uavcan_frame)
      50             : {
      51           0 :     can_frame sockcan_frame { uavcan_frame.id& AP_HAL::CANFrame::MaskExtID, uavcan_frame.dlc, { } };
      52           0 :     std::copy(uavcan_frame.data, uavcan_frame.data + uavcan_frame.dlc, sockcan_frame.data);
      53           0 :     if (uavcan_frame.isExtended()) {
      54           0 :         sockcan_frame.can_id |= CAN_EFF_FLAG;
      55             :     }
      56           0 :     if (uavcan_frame.isErrorFrame()) {
      57           0 :         sockcan_frame.can_id |= CAN_ERR_FLAG;
      58             :     }
      59           0 :     if (uavcan_frame.isRemoteTransmissionRequest()) {
      60           0 :         sockcan_frame.can_id |= CAN_RTR_FLAG;
      61             :     }
      62           0 :     return sockcan_frame;
      63             : }
      64             : 
      65           0 : static AP_HAL::CANFrame makeUavcanFrame(const can_frame& sockcan_frame)
      66             : {
      67           0 :     AP_HAL::CANFrame uavcan_frame(sockcan_frame.can_id & CAN_EFF_MASK, sockcan_frame.data, sockcan_frame.can_dlc);
      68           0 :     if (sockcan_frame.can_id & CAN_EFF_FLAG) {
      69           0 :         uavcan_frame.id |= AP_HAL::CANFrame::FlagEFF;
      70             :     }
      71           0 :     if (sockcan_frame.can_id & CAN_ERR_FLAG) {
      72           0 :         uavcan_frame.id |= AP_HAL::CANFrame::FlagERR;
      73             :     }
      74           0 :     if (sockcan_frame.can_id & CAN_RTR_FLAG) {
      75           0 :         uavcan_frame.id |= AP_HAL::CANFrame::FlagRTR;
      76             :     }
      77           0 :     return uavcan_frame;
      78             : }
      79             : 
      80           0 : bool CANIface::is_initialized() const
      81             : {
      82           0 :     return _initialized;
      83             : }
      84             : 
      85           0 : int CANIface::_openSocket(const std::string& iface_name)
      86             : {
      87           0 :     errno = 0;
      88             : 
      89           0 :     int s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
      90           0 :     if (s < 0) {
      91           0 :         return s;
      92             :     }
      93             : 
      94           0 :     std::shared_ptr<void> defer(&s, [](int* fd) { if (*fd >= 0) close(*fd); });
      95           0 :     const int ret = s;
      96             : 
      97             :     // Detect the iface index
      98           0 :     auto ifr = ifreq();
      99           0 :     if (iface_name.length() >= IFNAMSIZ) {
     100           0 :         errno = ENAMETOOLONG;
     101           0 :         return -1;
     102             :     }
     103           0 :     std::strncpy(ifr.ifr_name, iface_name.c_str(), iface_name.length());
     104           0 :     if (ioctl(s, SIOCGIFINDEX, &ifr) < 0 || ifr.ifr_ifindex < 0) {
     105           0 :         return -1;
     106             :     }
     107             : 
     108             :     // Bind to the specified CAN iface
     109             :     {
     110           0 :         auto addr = sockaddr_can();
     111           0 :         addr.can_family = AF_CAN;
     112           0 :         addr.can_ifindex = ifr.ifr_ifindex;
     113           0 :         if (bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
     114           0 :             return -1;
     115             :         }
     116             :     }
     117             : 
     118             :     // Configure
     119             :     {
     120           0 :         const int on = 1;
     121             :         // Timestamping
     122           0 :         if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0) {
     123           0 :             return -1;
     124             :         }
     125             :         // Socket loopback
     126           0 :         if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &on, sizeof(on)) < 0) {
     127           0 :             return -1;
     128             :         }
     129             :         // Non-blocking
     130           0 :         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
     131           0 :             return -1;
     132             :         }
     133             :     }
     134             : 
     135             :     // Validate the resulting socket
     136             :     {
     137           0 :         int socket_error = 0;
     138           0 :         socklen_t errlen = sizeof(socket_error);
     139           0 :         getsockopt(s, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&socket_error), &errlen);
     140           0 :         if (socket_error != 0) {
     141           0 :             errno = socket_error;
     142           0 :             return -1;
     143             :         }
     144             :     }
     145           0 :     s = -1;
     146           0 :     return ret;
     147           0 : }
     148             : 
     149           0 : int16_t CANIface::send(const AP_HAL::CANFrame& frame, const uint64_t tx_deadline,
     150             :                        const CANIface::CanIOFlags flags)
     151             : {
     152           0 :     CanTxItem tx_item {};
     153           0 :     tx_item.frame = frame;
     154           0 :     if (flags & Loopback) {
     155           0 :         tx_item.loopback = true;
     156             :     }
     157           0 :     if (flags & AbortOnError) {
     158           0 :         tx_item.abort_on_error = true;
     159             :     }
     160           0 :     tx_item.setup = true;
     161           0 :     tx_item.index = _tx_frame_counter;
     162           0 :     tx_item.deadline = tx_deadline;
     163           0 :     WITH_SEMAPHORE(sem);
     164           0 :     _tx_queue.emplace(tx_item);
     165           0 :     _tx_frame_counter++;
     166           0 :     stats.tx_requests++;
     167           0 :     _pollRead();     // Read poll is necessary because it can release the pending TX flag
     168           0 :     _pollWrite();
     169           0 :     return AP_HAL::CANIface::send(frame, tx_deadline, flags);
     170           0 : }
     171             : 
     172           0 : int16_t CANIface::receive(AP_HAL::CANFrame& out_frame, uint64_t& out_timestamp_us,
     173             :                           CANIface::CanIOFlags& out_flags)
     174             : {
     175           0 :     WITH_SEMAPHORE(sem);
     176           0 :     if (_rx_queue.empty()) {
     177           0 :         _pollRead();            // This allows to use the socket not calling poll() explicitly.
     178           0 :         if (_rx_queue.empty()) {
     179           0 :             return 0;
     180             :         }
     181             :     }
     182             :     {
     183           0 :         const CanRxItem& rx = _rx_queue.front();
     184           0 :         out_frame        = rx.frame;
     185           0 :         out_timestamp_us = rx.timestamp_us;
     186           0 :         out_flags        = rx.flags;
     187             :     }
     188           0 :     (void)_rx_queue.pop();
     189           0 :     if (sem_handle != nullptr) {
     190           0 :         sem_handle->signal();
     191             :     }
     192           0 :     return AP_HAL::CANIface::receive(out_frame, out_timestamp_us, out_flags);
     193           0 : }
     194             : 
     195           0 : bool CANIface::_hasReadyTx()
     196             : {
     197           0 :     WITH_SEMAPHORE(sem);
     198           0 :     return !_tx_queue.empty() && (_frames_in_socket_tx_queue < _max_frames_in_socket_tx_queue);
     199           0 : }
     200             : 
     201           0 : bool CANIface::_hasReadyRx()
     202             : {
     203           0 :     WITH_SEMAPHORE(sem);
     204           0 :     return !_rx_queue.empty();
     205           0 : }
     206             : 
     207           0 : void CANIface::_poll(bool read, bool write)
     208             : {
     209           0 :     if (read) {
     210           0 :         stats.num_poll_rx_events++;
     211           0 :         _pollRead(); // Read poll must be executed first because it may decrement _frames_in_socket_tx_queue
     212             :     }
     213           0 :     if (write) {
     214           0 :         stats.num_poll_tx_events++;
     215           0 :         _pollWrite();
     216             :     }
     217           0 : }
     218             : 
     219           0 : uint32_t CANIface::getErrorCount() const
     220             : {
     221           0 :     uint32_t ec = 0;
     222           0 :     for (auto& kv : _errors) { ec += kv.second; }
     223           0 :     return ec;
     224             : }
     225             : 
     226           0 : void CANIface::_pollWrite()
     227             : {
     228           0 :     while (_hasReadyTx()) {
     229           0 :         WITH_SEMAPHORE(sem);
     230           0 :         if (!_hasReadyTx()) {
     231           0 :             break;
     232             :         }
     233           0 :         const CanTxItem tx = _tx_queue.top();
     234           0 :         uint64_t curr_time = AP_HAL::micros64();
     235           0 :         if (tx.deadline >= curr_time) {
     236             :             // hal.console->printf("%x TDEAD: %lu CURRT: %lu DEL: %lu\n",tx.frame.id,  tx.deadline, curr_time, tx.deadline-curr_time);
     237           0 :             const int res = _write(tx.frame);
     238           0 :             if (res == 1) {                   // Transmitted successfully
     239           0 :                 _incrementNumFramesInSocketTxQueue();
     240           0 :                 if (tx.loopback) {
     241           0 :                     _pending_loopback_ids.insert(tx.frame.id);
     242             :                 }
     243           0 :                 stats.tx_success++;
     244           0 :                 stats.last_transmit_us = curr_time;
     245           0 :             } else if (res == 0) {            // Not transmitted, nor is it an error
     246           0 :                 stats.tx_overflow++;
     247           0 :                 break;                        // Leaving the loop, the frame remains enqueued for the next retry
     248             :             } else {                          // Transmission error
     249           0 :                 stats.tx_rejected++;
     250             :             }
     251             :         } else {
     252             :             // hal.console->printf("TDEAD: %lu CURRT: %lu DEL: %lu\n", tx.deadline, curr_time, curr_time-tx.deadline);
     253           0 :             stats.tx_timedout++;
     254             :         }
     255             : 
     256             :         // Removing the frame from the queue even if transmission failed
     257           0 :         (void)_tx_queue.pop();
     258           0 :     }
     259           0 : }
     260             : 
     261           0 : bool CANIface::_pollRead()
     262             : {
     263           0 :     uint8_t iterations_count = 0;
     264           0 :     while (iterations_count < CAN_MAX_POLL_ITERATIONS_COUNT)
     265             :     {
     266           0 :         iterations_count++;
     267           0 :         CanRxItem rx;
     268           0 :         rx.timestamp_us = AP_HAL::micros64();  // Monotonic timestamp is not required to be precise (unlike UTC)
     269           0 :         bool loopback = false;
     270           0 :         const int res = _read(rx.frame, rx.timestamp_us, loopback);
     271           0 :         if (res == 1) {
     272           0 :             bool accept = true;
     273           0 :             if (loopback) {           // We receive loopback for all CAN frames
     274           0 :                 _confirmSentFrame();
     275           0 :                 rx.flags |= Loopback;
     276           0 :                 accept = _wasInPendingLoopbackSet(rx.frame);
     277           0 :                 stats.tx_confirmed++;
     278             :             }
     279           0 :             if (accept) {
     280           0 :                 WITH_SEMAPHORE(sem);
     281           0 :                 _rx_queue.push(rx);
     282           0 :                 stats.rx_received++;
     283           0 :                 return true;
     284           0 :             }
     285           0 :         } else if (res == 0) {
     286           0 :             break;
     287             :         } else {
     288           0 :             stats.rx_errors++;
     289           0 :             break;
     290             :         }
     291             :     }
     292           0 :     return false;
     293             : }
     294             : 
     295           0 : int CANIface::_write(const AP_HAL::CANFrame& frame) const
     296             : {
     297           0 :     if (_fd < 0) {
     298           0 :         return -1;
     299             :     }
     300           0 :     errno = 0;
     301             : 
     302           0 :     const can_frame sockcan_frame = makeSocketCanFrame(frame);
     303             : 
     304           0 :     const int res = write(_fd, &sockcan_frame, sizeof(sockcan_frame));
     305           0 :     if (res <= 0) {
     306           0 :         if (errno == ENOBUFS || errno == EAGAIN) {  // Writing is not possible atm, not an error
     307           0 :             return 0;
     308             :         }
     309           0 :         return res;
     310             :     }
     311           0 :     if (res != sizeof(sockcan_frame)) {
     312           0 :         return -1;
     313             :     }
     314           0 :     return 1;
     315             : }
     316             : 
     317             : 
     318           0 : int CANIface::_read(AP_HAL::CANFrame& frame, uint64_t& timestamp_us, bool& loopback) const
     319             : {
     320           0 :     if (_fd < 0) {
     321           0 :         return -1;
     322             :     }
     323           0 :     auto iov = iovec();
     324           0 :     auto sockcan_frame = can_frame();
     325           0 :     iov.iov_base = &sockcan_frame;
     326           0 :     iov.iov_len  = sizeof(sockcan_frame);
     327             :     union {
     328             :         uint8_t data[CMSG_SPACE(sizeof(::timeval))];
     329             :         struct cmsghdr align;
     330             :     } control;
     331             : 
     332           0 :     auto msg = msghdr();
     333           0 :     msg.msg_iov    = &iov;
     334           0 :     msg.msg_iovlen = 1;
     335           0 :     msg.msg_control = control.data;
     336           0 :     msg.msg_controllen = sizeof(control.data);
     337             : 
     338           0 :     const int res = recvmsg(_fd, &msg, MSG_DONTWAIT);
     339           0 :     if (res <= 0) {
     340           0 :         return (res < 0 && errno == EWOULDBLOCK) ? 0 : res;
     341             :     }
     342             :     /*
     343             :      * Flags
     344             :      */
     345           0 :     loopback = (msg.msg_flags & static_cast<int>(MSG_CONFIRM)) != 0;
     346             : 
     347           0 :     frame = makeUavcanFrame(sockcan_frame);
     348             :     /*
     349             :      * Timestamp
     350             :      */
     351           0 :     timestamp_us = AP_HAL::micros64();
     352           0 :     return 1;
     353             : }
     354             : 
     355             : // Might block forever, only to be used for testing
     356           0 : void CANIface::flush_tx()
     357             : {
     358           0 :     WITH_SEMAPHORE(sem);
     359             :     do {
     360           0 :         _updateDownStatusFromPollResult(_pollfd);
     361           0 :         _poll(true, true);
     362           0 :     } while(!_tx_queue.empty() && !_down);
     363           0 : }
     364             : 
     365           0 : void CANIface::clear_rx()
     366             : {
     367           0 :     WITH_SEMAPHORE(sem);
     368             :     // Clean Rx Queue
     369           0 :     std::queue<CanRxItem> empty;
     370           0 :     std::swap( _rx_queue, empty );
     371           0 : }
     372             : 
     373           0 : void CANIface::_incrementNumFramesInSocketTxQueue()
     374             : {
     375           0 :     _frames_in_socket_tx_queue++;
     376           0 : }
     377             : 
     378           0 : void CANIface::_confirmSentFrame()
     379             : {
     380           0 :     if (_frames_in_socket_tx_queue > 0) {
     381           0 :         _frames_in_socket_tx_queue--;
     382             :     }
     383           0 : }
     384             : 
     385           0 : bool CANIface::_wasInPendingLoopbackSet(const AP_HAL::CANFrame& frame)
     386             : {
     387           0 :     if (_pending_loopback_ids.count(frame.id) > 0) {
     388           0 :         _pending_loopback_ids.erase(frame.id);
     389           0 :         return true;
     390             :     }
     391           0 :     return false;
     392             : }
     393             : 
     394           0 : void CANIface::_updateDownStatusFromPollResult(const pollfd& pfd)
     395             : {
     396           0 :     if (!_down && (pfd.revents & POLLERR)) {
     397           0 :         int error = 0;
     398           0 :         socklen_t errlen = sizeof(error);
     399           0 :         getsockopt(pfd.fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&error), &errlen);
     400             : 
     401           0 :         _down= error == ENETDOWN || error == ENODEV;
     402           0 :         stats.num_downs++;
     403             :     }
     404           0 : }
     405             : 
     406           0 : bool CANIface::init(const uint32_t bitrate)
     407             : {
     408             :     char iface_name[16];
     409             : #if HAL_LINUX_USE_VIRTUAL_CAN
     410           0 :     sprintf(iface_name, "vcan%u", _self_index);
     411             : #else
     412             :     sprintf(iface_name, "can%u", _self_index);
     413             : #endif
     414           0 :     if (_initialized) {
     415           0 :         return _initialized;
     416             :     }
     417           0 :     bitrate_ = bitrate;
     418             :     // TODO: Add possibility change bitrate
     419           0 :     _fd = _openSocket(iface_name);
     420           0 :     if (_fd > 0) {
     421           0 :         _bitrate = bitrate;
     422           0 :         _initialized = true;
     423             :     } else {
     424           0 :         _initialized = false;
     425             :     }
     426           0 :     return _initialized;
     427             : }
     428             : 
     429           0 : bool CANIface::select(bool &read_select, bool &write_select,
     430             :                         const AP_HAL::CANFrame* const pending_tx, uint64_t blocking_deadline)
     431             : {
     432             :     // Detecting whether we need to block at all
     433           0 :     bool need_block = !write_select;    // Write queue is infinite
     434             : 
     435           0 :     if (read_select && _hasReadyRx()) {
     436           0 :         need_block = false;
     437             :     }
     438             : 
     439           0 :     if (need_block) {
     440           0 :         if (_down) {
     441           0 :             return false;
     442             :         } else {
     443           0 :             _pollfd.fd = _fd;
     444           0 :             _pollfd.events |= POLLIN;
     445           0 :             stats.num_rx_poll_req++;
     446           0 :             if (_hasReadyTx() && write_select) {
     447           0 :                 _pollfd.events |= POLLOUT;
     448           0 :                 stats.num_tx_poll_req++;
     449             :             }
     450             :         }
     451           0 :         const uint64_t now_us = AP_HAL::micros64();
     452           0 :         if (sem_handle != nullptr && blocking_deadline > now_us) {
     453           0 :             IGNORE_RETURN(sem_handle->wait(blocking_deadline - now_us));
     454             :         }
     455             :     }
     456             : 
     457             :     // Writing the output masks
     458           0 :     if (!_down) {
     459           0 :         write_select = true;     // Always ready to write if not down
     460             :     } else {
     461           0 :         write_select = false;
     462             :     }
     463           0 :     if (_hasReadyRx()) {
     464           0 :         read_select = true;      // Readability depends only on RX buf, even if down
     465             :     } else {
     466           0 :         read_select = false;
     467             :     }
     468             : 
     469             :     // Return value is irrelevant as long as it's non-negative
     470           0 :     return true;
     471             : }
     472             : 
     473           0 : bool CANIface::set_event_handle(AP_HAL::BinarySemaphore *handle)
     474             : {
     475           0 :     sem_handle = handle;
     476           0 :     return true;
     477             : }
     478             : 
     479             : 
     480           0 : void CANIface::get_stats(ExpandingString &str)
     481             : {
     482           0 :     str.printf("tx_requests:    %u\n"
     483             :                "tx_rejected:    %u\n"
     484             :                "tx_overflow:    %u\n"
     485             :                "tx_confirmed:   %u\n"
     486             :                "tx_success:     %u\n"
     487             :                "tx_timedout:    %u\n"
     488             :                "rx_received:    %u\n"
     489             :                "rx_errors:      %u\n"
     490             :                "num_downs:      %u\n"
     491             :                "num_rx_poll_req:  %u\n"
     492             :                "num_tx_poll_req:  %u\n"
     493             :                "num_poll_waits:   %u\n"
     494             :                "num_poll_tx_events: %u\n"
     495             :                "num_poll_rx_events: %u\n",
     496             :                stats.tx_requests,
     497             :                stats.tx_rejected,
     498             :                stats.tx_overflow,
     499             :                stats.tx_confirmed,
     500             :                stats.tx_success,
     501             :                stats.tx_timedout,
     502             :                stats.rx_received,
     503             :                stats.rx_errors,
     504             :                stats.num_downs,
     505             :                stats.num_rx_poll_req,
     506             :                stats.num_tx_poll_req,
     507             :                stats.num_poll_waits,
     508             :                stats.num_poll_tx_events,
     509             :                stats.num_poll_rx_events);
     510           0 : }
     511             : 
     512             : #endif

Generated by: LCOV version 1.14