LCOV - code coverage report
Current view: top level - platforms/posix/bsp/socketCanTransceiver/src/can - SocketCanTransceiver.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 6.2 % 144 9
Test Date: 2026-11-13 15:04:32 Functions: 4.3 % 23 1

            Line data    Source code
       1              : /********************************************************************************
       2              :  * Copyright (c) 2024 Accenture
       3              :  *
       4              :  * This program and the accompanying materials are made available under the
       5              :  * terms of the Apache License Version 2.0 which is available at
       6              :  * https://www.apache.org/licenses/LICENSE-2.0
       7              :  *
       8              :  * SPDX-License-Identifier: Apache-2.0
       9              :  ********************************************************************************/
      10              : 
      11              : #include "can/SocketCanTransceiver.h"
      12              : 
      13              : #include <can/CanLogger.h>
      14              : #include <can/canframes/ICANFrameSentListener.h>
      15              : #include <linux/can.h>
      16              : #include <linux/can/raw.h>
      17              : #include <net/if.h>
      18              : #include <sys/ioctl.h>
      19              : #include <sys/socket.h>
      20              : 
      21              : #include <fcntl.h>
      22              : #include <signal.h>
      23              : #include <unistd.h>
      24              : 
      25              : #include <etl/char_traits.h>
      26              : #include <etl/error_handler.h>
      27              : #include <etl/memory.h>
      28              : #include <etl/span.h>
      29              : #include <etl/type_traits.h>
      30              : #include <etl/utility.h>
      31              : #include <sys/types.h>
      32              : 
      33              : static_assert(
      34              :     ::etl::is_standard_layout<::can::CANFrame>::value
      35              :         && ::etl::is_trivially_destructible<::can::CANFrame>::value,
      36              :     "check for UB while passing through TxQueue");
      37              : 
      38              : namespace can
      39              : {
      40              : 
      41              : using ::util::logger::CAN;
      42              : using ::util::logger::Logger;
      43              : 
      44              : namespace
      45              : {
      46              : 
      47              : template<typename F>
      48            0 : void signalGuarded(F&& function)
      49              : {
      50              :     sigset_t set, oldSet;
      51            0 :     sigfillset(&set);
      52            0 :     pthread_sigmask(SIG_SETMASK, &set, &oldSet);
      53            0 :     ::etl::forward<F>(function)();
      54            0 :     pthread_sigmask(SIG_SETMASK, &oldSet, nullptr);
      55            0 : }
      56              : 
      57              : } // namespace
      58              : 
      59              : // needed if ODR-used
      60              : size_t const SocketCanTransceiver::TX_QUEUE_SIZE_BYTES;
      61              : 
      62            1 : SocketCanTransceiver::SocketCanTransceiver(DeviceConfig const& config)
      63            1 : : AbstractCANTransceiver(config.busId)
      64            1 : , _txQueue()
      65            1 : , _txReader(_txQueue)
      66            1 : , _txWriter(_txQueue)
      67            1 : , _config(config)
      68            1 : , _fileDescriptor(-1)
      69            2 : , _writable(false)
      70            1 : {}
      71              : 
      72            0 : ICanTransceiver::ErrorCode SocketCanTransceiver::init()
      73              : {
      74            0 :     if (!isInState(State::CLOSED))
      75              :     {
      76            0 :         return ErrorCode::CAN_ERR_ILLEGAL_STATE;
      77              :     }
      78            0 :     setState(State::INITIALIZED);
      79            0 :     return ErrorCode::CAN_ERR_OK;
      80              : }
      81              : 
      82            0 : ICanTransceiver::ErrorCode SocketCanTransceiver::open()
      83              : {
      84            0 :     if (!isInState(State::INITIALIZED))
      85              :     {
      86            0 :         return ErrorCode::CAN_ERR_ILLEGAL_STATE;
      87              :     }
      88            0 :     signalGuarded([this] { guardedOpen(); });
      89            0 :     setState(State::OPEN);
      90            0 :     _writable.store(true);
      91            0 :     return ErrorCode::CAN_ERR_OK;
      92              : }
      93              : 
      94            0 : ICanTransceiver::ErrorCode SocketCanTransceiver::open(CANFrame const& /* frame */)
      95              : {
      96            0 :     ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("not implemented"));
      97              :     return ErrorCode::CAN_ERR_ILLEGAL_STATE;
      98              : }
      99              : 
     100            0 : ICanTransceiver::ErrorCode SocketCanTransceiver::close()
     101              : {
     102            0 :     if (!isInState(State::OPEN) && !isInState(State::MUTED))
     103              :     {
     104            0 :         return ErrorCode::CAN_ERR_ILLEGAL_STATE;
     105              :     }
     106            0 :     signalGuarded([this] { guardedClose(); });
     107            0 :     _writable.store(false);
     108            0 :     setState(State::CLOSED);
     109            0 :     return ErrorCode::CAN_ERR_OK;
     110              : }
     111              : 
     112            0 : void SocketCanTransceiver::shutdown() {}
     113              : 
     114            0 : ICanTransceiver::ErrorCode SocketCanTransceiver::write(CANFrame const& frame)
     115              : {
     116            0 :     return writeImpl(frame, nullptr);
     117              : }
     118              : 
     119              : ICanTransceiver::ErrorCode
     120            0 : SocketCanTransceiver::write(CANFrame const& frame, ICANFrameSentListener& listener)
     121              : {
     122            0 :     return writeImpl(frame, &listener);
     123              : }
     124              : 
     125              : ICanTransceiver::ErrorCode
     126            0 : SocketCanTransceiver::writeImpl(CANFrame const& frame, ICANFrameSentListener* listener)
     127              : {
     128            0 :     if (!_writable.load(::etl::memory_order_relaxed))
     129              :     {
     130            0 :         return ErrorCode::CAN_ERR_ILLEGAL_STATE;
     131              :     }
     132            0 :     auto memory = _txWriter.allocate(TX_ELEMENT_SIZE_BYTES);
     133            0 :     if (memory.size() < TX_ELEMENT_SIZE_BYTES)
     134              :     {
     135            0 :         return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL;
     136              :     }
     137              :     // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise copy into the TxQueue
     138            0 :     ::etl::mem_copy(reinterpret_cast<uint8_t const*>(&frame), sizeof(frame), memory.data());
     139            0 :     ::etl::mem_copy(
     140            0 :         reinterpret_cast<uint8_t const*>(&listener), sizeof(void*), memory.data() + sizeof(frame));
     141              :     // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
     142            0 :     _txWriter.commit();
     143            0 :     return ErrorCode::CAN_ERR_OK;
     144              : }
     145              : 
     146            0 : ICanTransceiver::ErrorCode SocketCanTransceiver::mute()
     147              : {
     148            0 :     if (!isInState(State::OPEN))
     149              :     {
     150            0 :         return ErrorCode::CAN_ERR_ILLEGAL_STATE;
     151              :     }
     152            0 :     _writable.store(false);
     153            0 :     setState(State::MUTED);
     154            0 :     return ErrorCode::CAN_ERR_OK;
     155              : }
     156              : 
     157            0 : ICanTransceiver::ErrorCode SocketCanTransceiver::unmute()
     158              : {
     159            0 :     if (!isInState(State::MUTED))
     160              :     {
     161            0 :         return ErrorCode::CAN_ERR_ILLEGAL_STATE;
     162              :     }
     163            0 :     setState(State::OPEN);
     164            0 :     _writable.store(true);
     165            0 :     return ErrorCode::CAN_ERR_OK;
     166              : }
     167              : 
     168            0 : uint32_t SocketCanTransceiver::getBaudrate() const { return 500000U; }
     169              : 
     170            0 : uint16_t SocketCanTransceiver::getHwQueueTimeout() const { return 1U; }
     171              : 
     172            0 : void SocketCanTransceiver::run(int maxSentPerRun, int maxReceivedPerRun)
     173              : {
     174            0 :     signalGuarded([this, maxSentPerRun, maxReceivedPerRun]
     175            0 :                   { guardedRun(maxSentPerRun, maxReceivedPerRun); });
     176            0 : }
     177              : 
     178            0 : void SocketCanTransceiver::guardedOpen()
     179              : {
     180              :     // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg): Logger API is variadic by design.
     181            0 :     char const* const name = _config.name;
     182            0 :     int error              = 0;
     183            0 :     int const fd           = socket(PF_CAN, SOCK_RAW, CAN_RAW);
     184            0 :     if (fd < 0)
     185              :     {
     186            0 :         Logger::error(
     187              :             CAN, "[SocketCanTransceiver] Failed to create socket (node=%s, error=%d)", name, fd);
     188            0 :         return;
     189              :     }
     190              : 
     191              :     struct ifreq ifr;
     192            0 :     etl::strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) / sizeof(ifr.ifr_name[0]));
     193            0 :     error = ioctl(fd, SIOCGIFINDEX, &ifr);
     194            0 :     if (error < 0)
     195              :     {
     196            0 :         Logger::error(
     197              :             CAN, "[SocketCanTransceiver] Failed to ioctl socket (node=%s, error=%d)", name, error);
     198            0 :         return;
     199              :     }
     200              : 
     201            0 :     if (_config.enableCanFd)
     202              :     {
     203            0 :         int const enable_canfd = 1;
     204            0 :         error = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd));
     205            0 :         if (error < 0)
     206              :         {
     207            0 :             Logger::error(
     208              :                 CAN,
     209              :                 "[SocketCanTransceiver] Failed to setsockopt socket (node=%s, error=%d)",
     210              :                 name,
     211              :                 error);
     212            0 :             return;
     213              :         }
     214              :     }
     215              : 
     216            0 :     error = fcntl(fd, F_SETFL, O_NONBLOCK);
     217            0 :     if (error < 0)
     218              :     {
     219            0 :         Logger::error(
     220              :             CAN,
     221              :             "[SocketCanTransceiver] Failed to switch to non-blocking mode (node=%s, error=%d)",
     222              :             name,
     223              :             error);
     224            0 :         return;
     225              :     }
     226              : 
     227              :     struct sockaddr_can addr;
     228              :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise zero-fill
     229            0 :     ::etl::mem_set(reinterpret_cast<uint8_t*>(&addr), sizeof(addr), static_cast<uint8_t>(0));
     230            0 :     addr.can_family  = AF_CAN;
     231            0 :     addr.can_ifindex = ifr.ifr_ifindex;
     232              :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): POSIX bind() requires sockaddr*
     233            0 :     error            = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
     234            0 :     if (error < 0)
     235              :     {
     236            0 :         Logger::error(
     237              :             CAN, "[SocketCanTransceiver] Failed to bind socket (node=%s, error=%d)", name, error);
     238            0 :         return;
     239              :     }
     240              : 
     241            0 :     _fileDescriptor = fd;
     242              :     // NOLINTEND(cppcoreguidelines-pro-type-vararg)
     243              : }
     244              : 
     245            0 : void SocketCanTransceiver::guardedClose()
     246              : {
     247            0 :     ::close(_fileDescriptor);
     248            0 :     _fileDescriptor = -1;
     249            0 : }
     250              : 
     251            0 : void SocketCanTransceiver::guardedRun(int maxSentPerRun, int maxReceivedPerRun)
     252              : {
     253              :     // MUTED condition does not affect the messages already in the write queue;
     254              :     // the idea is that once we confirmed that we had accepted the message for delivery,
     255              :     // we shall try to deliver it.
     256            0 :     for (int count = 0; count < maxSentPerRun; ++count)
     257              :     {
     258            0 :         auto memory = _txReader.peek();
     259            0 :         if (memory.size() < TX_ELEMENT_SIZE_BYTES)
     260              :         {
     261            0 :             break;
     262              :         }
     263            0 :         CANFrame canFrame;
     264              :         // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise copy from the TxQueue
     265            0 :         ::etl::mem_copy(memory.data(), sizeof(canFrame), reinterpret_cast<uint8_t*>(&canFrame));
     266            0 :         ICANFrameSentListener* listener = nullptr;
     267            0 :         ::etl::mem_copy(
     268            0 :             memory.data() + sizeof(canFrame), sizeof(void*), reinterpret_cast<uint8_t*>(&listener));
     269              :         // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
     270            0 :         _txReader.release();
     271              : 
     272            0 :         uint8_t const length = static_cast<uint8_t>(canFrame.getPayloadLength());
     273            0 :         bool const sendAsFd  = _config.enableCanFd;
     274              : 
     275              :         // canfd_frame is layout-compatible with can_frame for the fields we set
     276              :         // (can_id, len, data); byte 5 (flags) maps to can_frame::__pad for
     277              :         // classical frames and must remain 0, which the zero-fill guarantees.
     278              :         canfd_frame outFrame;
     279            0 :         ::etl::mem_set(
     280              :             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise zero-fill
     281              :             reinterpret_cast<uint8_t*>(&outFrame),
     282              :             sizeof(outFrame),
     283              :             static_cast<uint8_t>(0));
     284            0 :         outFrame.can_id = canFrame.getId();
     285            0 :         outFrame.len    = length;
     286            0 :         if (sendAsFd && _config.enableBitRateSwitch)
     287              :         {
     288            0 :             outFrame.flags |= CANFD_BRS;
     289              :         }
     290            0 :         ::etl::mem_copy(canFrame.getPayload(), length, outFrame.data);
     291              : 
     292              :         // MTU selects the on-wire frame type: CAN_MTU for classical, CANFD_MTU for FD.
     293            0 :         size_t const mtu           = sendAsFd ? CANFD_MTU : CAN_MTU;
     294            0 :         ssize_t const bytesWritten = ::write(_fileDescriptor, &outFrame, mtu);
     295            0 :         if (bytesWritten != static_cast<ssize_t>(mtu))
     296              :         {
     297            0 :             break;
     298              :         }
     299            0 :         if (listener != nullptr)
     300              :         {
     301            0 :             listener->canFrameSent(canFrame);
     302              :         }
     303            0 :         notifySentListeners(canFrame);
     304              :     }
     305              : 
     306            0 :     for (int count = 0; count < maxReceivedPerRun; ++count)
     307              :     {
     308              :         canfd_frame inFrame;
     309            0 :         ::etl::mem_set(
     310              :             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise zero-fill
     311              :             reinterpret_cast<uint8_t*>(&inFrame),
     312              :             sizeof(inFrame),
     313              :             static_cast<uint8_t>(0));
     314            0 :         ssize_t const bytesRead = ::read(_fileDescriptor, &inFrame, CANFD_MTU);
     315            0 :         if (bytesRead < 0)
     316              :         {
     317            0 :             break;
     318              :         }
     319            0 :         if (bytesRead != CAN_MTU && bytesRead != CANFD_MTU)
     320              :         {
     321              :             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Logger API is variadic by design.
     322            0 :             Logger::warn(
     323              :                 CAN,
     324              :                 "[SocketCanTransceiver] discarded frame with unexpected size=%d",
     325              :                 static_cast<int>(bytesRead));
     326            0 :             continue;
     327              :         }
     328              : 
     329            0 :         CANFrame canFrame;
     330            0 :         canFrame.setId(inFrame.can_id);
     331            0 :         canFrame.setPayload(inFrame.data, inFrame.len);
     332            0 :         canFrame.setTimestamp(0);
     333              : 
     334            0 :         notifyListeners(canFrame);
     335              :     }
     336            0 : }
     337              : 
     338              : } // namespace can
        

Generated by: LCOV version 2.0-1