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

          Line data    Source code
       1             : /*
       2             :  * This file is free software: you can redistribute it and/or modify it
       3             :  * under the terms of the GNU General Public License as published by the
       4             :  * Free Software Foundation, either version 3 of the License, or
       5             :  * (at your option) any later version.
       6             :  *
       7             :  * This file is distributed in the hope that it will be useful, but
       8             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      10             :  * See the GNU General Public License for more details.
      11             :  *
      12             :  * You should have received a copy of the GNU General Public License along
      13             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : #include "AP_MAVLinkCAN.h"
      17             : #include <AP_HAL/utility/sparse-endian.h>
      18             : #include <AP_Common/sorting.h>
      19             : 
      20             : #if AP_MAVLINKCAN_ENABLED
      21             : 
      22             : extern const AP_HAL::HAL& hal;
      23             : 
      24             : static AP_MAVLinkCAN *singleton;
      25             : 
      26           0 : AP_MAVLinkCAN *AP_MAVLinkCAN::ensure_singleton()
      27             : {
      28           0 :     if (singleton == nullptr) {
      29           0 :         singleton = NEW_NOTHROW AP_MAVLinkCAN();
      30             :     }
      31           0 :     return singleton;
      32             : }
      33             : 
      34           0 : bool AP_MAVLinkCAN::handle_can_forward(mavlink_channel_t chan, const mavlink_command_int_t &packet, const mavlink_message_t &msg)
      35             : {
      36           0 :     auto *s = ensure_singleton();
      37           0 :     if (s == nullptr) {
      38           0 :         return false;
      39             :     }
      40           0 :     return singleton->_handle_can_forward(chan, packet, msg);
      41             : }
      42             : 
      43           0 : void AP_MAVLinkCAN::handle_can_frame(const mavlink_message_t &msg)
      44             : {
      45           0 :     auto *s = ensure_singleton();
      46           0 :     if (s == nullptr) {
      47           0 :         return;
      48             :     }
      49           0 :     singleton->_handle_can_frame(msg);
      50             : }
      51             : 
      52           0 : void AP_MAVLinkCAN::handle_can_filter_modify(const mavlink_message_t &msg)
      53             : {
      54           0 :     auto *s = ensure_singleton();
      55           0 :     if (s == nullptr) {
      56           0 :         return;
      57             :     }
      58           0 :     singleton->_handle_can_filter_modify(msg);
      59             : }
      60             : 
      61             : 
      62             : /*
      63             :   handle MAV_CMD_CAN_FORWARD mavlink long command
      64             :  */
      65           0 : bool AP_MAVLinkCAN::_handle_can_forward(mavlink_channel_t chan, const mavlink_command_int_t &packet, const mavlink_message_t &msg)
      66             : {
      67           0 :     WITH_SEMAPHORE(can_forward.sem);
      68           0 :     const int8_t bus = int8_t(packet.param1)-1;
      69             : 
      70           0 :     if (bus == -1) {
      71             :         /*
      72             :           a request to stop forwarding
      73             :          */
      74           0 :         if (can_forward.callback_id != 0) {
      75           0 :             hal.can[can_forward.callback_bus]->unregister_frame_callback(can_forward.callback_id);
      76           0 :             can_forward.callback_id = 0;
      77             :         }
      78           0 :         return true;
      79             :     }
      80             : 
      81           0 :     if (bus < 0 || bus >= HAL_NUM_CAN_IFACES || hal.can[bus] == nullptr) {
      82           0 :         return false;
      83             :     }
      84             : 
      85           0 :     if (can_forward.callback_id != 0 && can_forward.callback_bus != bus && can_forward.callback_bus < HAL_NUM_CAN_IFACES) {
      86             :         /*
      87             :           the client is changing which bus they are monitoring, unregister from the previous bus
      88             :          */
      89           0 :         hal.can[can_forward.callback_bus]->unregister_frame_callback(can_forward.callback_id);
      90           0 :         can_forward.callback_id = 0;
      91             :     }
      92             : 
      93           0 :     if (can_forward.callback_id == 0 &&
      94           0 :         !hal.can[bus]->register_frame_callback(
      95           0 :             FUNCTOR_BIND_MEMBER(&AP_MAVLinkCAN::can_frame_callback, void, uint8_t, const AP_HAL::CANFrame &, AP_HAL::CANIface::CanIOFlags), can_forward.callback_id)) {
      96             :         // failed to register the callback
      97           0 :         return false;
      98             :     }
      99             : 
     100           0 :     can_forward.callback_bus = bus;
     101           0 :     can_forward.last_callback_enable_ms = AP_HAL::millis();
     102           0 :     can_forward.chan = chan;
     103           0 :     can_forward.system_id = msg.sysid;
     104           0 :     can_forward.component_id = msg.compid;
     105             : 
     106           0 :     return true;
     107           0 : }
     108             : 
     109             : /*
     110             :   handle a CAN_FRAME packet
     111             :  */
     112           0 : void AP_MAVLinkCAN::_handle_can_frame(const mavlink_message_t &msg)
     113             : {
     114           0 :     if (frame_buffer == nullptr) {
     115             :         // allocate frame buffer
     116             :         // 20 is good for firmware upload
     117           0 :         uint8_t buffer_size = 20;
     118           0 :         WITH_SEMAPHORE(frame_buffer_sem);
     119           0 :         while (frame_buffer == nullptr && buffer_size > 0) {
     120             :             // we'd like 20 frames, but will live with less
     121           0 :             frame_buffer = NEW_NOTHROW ObjectBuffer<BufferFrame>(buffer_size);
     122           0 :             if (frame_buffer != nullptr && frame_buffer->get_size() != 0) {
     123             :                 // register a callback for when frames can't be sent immediately
     124           0 :                 hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_MAVLinkCAN::process_frame_buffer, void));
     125           0 :                 break;
     126             :             }
     127           0 :             delete frame_buffer;
     128           0 :             frame_buffer = nullptr;
     129           0 :             buffer_size /= 2;
     130             :         }
     131           0 :         if (frame_buffer == nullptr) {
     132             :             // discard the frames
     133           0 :             return;
     134             :         }
     135           0 :     }
     136             : 
     137           0 :     switch (msg.msgid) {
     138           0 :     case MAVLINK_MSG_ID_CAN_FRAME: {
     139             :         mavlink_can_frame_t p;
     140           0 :         mavlink_msg_can_frame_decode(&msg, &p);
     141           0 :         if (p.bus >= HAL_NUM_CAN_IFACES || hal.can[p.bus] == nullptr) {
     142           0 :             return;
     143             :         }
     144             :         struct BufferFrame frame {
     145           0 :             bus : p.bus,
     146           0 :             frame : AP_HAL::CANFrame(p.id, p.data, p.len)
     147           0 :         };
     148             :         {
     149           0 :             WITH_SEMAPHORE(frame_buffer_sem);
     150           0 :             frame_buffer->push(frame);
     151           0 :         }
     152           0 :         break;
     153             :     }
     154             : #if HAL_CANFD_SUPPORTED
     155           0 :     case MAVLINK_MSG_ID_CANFD_FRAME: {
     156             :         mavlink_canfd_frame_t p;
     157           0 :         mavlink_msg_canfd_frame_decode(&msg, &p);
     158           0 :         if (p.bus >= HAL_NUM_CAN_IFACES || hal.can[p.bus] == nullptr) {
     159           0 :             return;
     160             :         }
     161             :         struct BufferFrame frame {
     162           0 :             bus : p.bus,
     163           0 :             frame : AP_HAL::CANFrame(p.id, p.data, p.len, true)
     164           0 :         };
     165             :         {
     166           0 :             WITH_SEMAPHORE(frame_buffer_sem);
     167           0 :             frame_buffer->push(frame);
     168           0 :         }
     169           0 :         break;
     170             :     }
     171             : #endif
     172             :     }
     173           0 :     process_frame_buffer();
     174             : }
     175             : 
     176             : /*
     177             :   process the frame buffer
     178             :  */
     179           0 : void AP_MAVLinkCAN::process_frame_buffer()
     180             : {
     181           0 :     while (frame_buffer) {
     182           0 :         WITH_SEMAPHORE(frame_buffer_sem);
     183           0 :         struct BufferFrame frame;
     184           0 :         const uint16_t timeout_us = 2000;
     185           0 :         if (!frame_buffer->peek(frame)) {
     186             :             // no frames in the queue
     187           0 :             break;
     188             :         }
     189           0 :         const int16_t retcode = hal.can[frame.bus]->send(frame.frame,
     190           0 :                                                             AP_HAL::micros64() + timeout_us,
     191             :                                                             AP_HAL::CANIface::IsForwardedFrame);
     192           0 :         if (retcode == 0) {
     193             :             // no space in the CAN output slots, try again later
     194           0 :             break;
     195             :         }
     196             :         // retcode == 1 means sent, -1 means a frame that can't be
     197             :         // sent. Either way we should remove from the queue
     198           0 :         frame_buffer->pop();
     199           0 :     }
     200           0 : }
     201             : 
     202             : /*
     203             :   handle a CAN_FILTER_MODIFY packet
     204             :  */
     205           0 : void AP_MAVLinkCAN::_handle_can_filter_modify(const mavlink_message_t &msg)
     206             : {
     207             :     mavlink_can_filter_modify_t p;
     208           0 :     mavlink_msg_can_filter_modify_decode(&msg, &p);
     209           0 :     const int8_t bus = int8_t(p.bus)-1;
     210           0 :     if (bus < 0 || bus >= HAL_NUM_CAN_IFACES || hal.can[bus] == nullptr) {
     211           0 :         return;
     212             :     }
     213           0 :     if (p.num_ids > ARRAY_SIZE(p.ids)) {
     214           0 :         return;
     215             :     }
     216           0 :     uint16_t *new_ids = nullptr;
     217           0 :     uint16_t num_new_ids = 0;
     218           0 :     WITH_SEMAPHORE(can_forward.sem);
     219             : 
     220             :     // sort the list, so we can bisection search and the array
     221             :     // operations below are efficient
     222           0 :     insertion_sort_uint16(p.ids, p.num_ids);
     223             :     
     224           0 :     switch (p.operation) {
     225           0 :     case CAN_FILTER_REPLACE: {
     226           0 :         if (p.num_ids == 0) {
     227           0 :             can_forward.num_filter_ids = 0;
     228           0 :             delete[] can_forward.filter_ids;
     229           0 :             can_forward.filter_ids = nullptr;
     230           0 :             return;
     231             :         }
     232           0 :         if (p.num_ids == can_forward.num_filter_ids &&
     233           0 :             memcmp(p.ids, can_forward.filter_ids, p.num_ids*sizeof(uint16_t)) == 0) {
     234             :             // common case of replacing with identical list
     235           0 :             return;
     236             :         }
     237           0 :         new_ids = NEW_NOTHROW uint16_t[p.num_ids];
     238           0 :         if (new_ids != nullptr) {
     239           0 :             num_new_ids = p.num_ids;
     240           0 :             memcpy((void*)new_ids, (const void *)p.ids, p.num_ids*sizeof(uint16_t));
     241             :         }
     242           0 :         break;
     243             :     }
     244           0 :     case CAN_FILTER_ADD: {
     245           0 :         if (common_list_uint16(can_forward.filter_ids, can_forward.num_filter_ids,
     246           0 :                                p.ids, p.num_ids) == p.num_ids) {
     247             :             // nothing changing
     248           0 :             return;
     249             :         }
     250           0 :         new_ids = NEW_NOTHROW uint16_t[can_forward.num_filter_ids+p.num_ids];
     251           0 :         if (new_ids == nullptr) {
     252           0 :             return;
     253             :         }
     254           0 :         if (can_forward.num_filter_ids != 0) {
     255           0 :             memcpy(new_ids, can_forward.filter_ids, can_forward.num_filter_ids*sizeof(uint16_t));
     256             :         }
     257           0 :         memcpy(&new_ids[can_forward.num_filter_ids], p.ids, p.num_ids*sizeof(uint16_t));
     258           0 :         insertion_sort_uint16(new_ids, can_forward.num_filter_ids+p.num_ids);
     259           0 :         num_new_ids = remove_duplicates_uint16(new_ids, can_forward.num_filter_ids+p.num_ids);
     260           0 :         break;
     261             :     }
     262           0 :     case CAN_FILTER_REMOVE: {
     263           0 :         if (common_list_uint16(can_forward.filter_ids, can_forward.num_filter_ids,
     264           0 :                                p.ids, p.num_ids) == 0) {
     265             :             // nothing changing
     266           0 :             return;
     267             :         }
     268           0 :         can_forward.num_filter_ids = remove_list_uint16(can_forward.filter_ids, can_forward.num_filter_ids,
     269           0 :                                                         p.ids, p.num_ids);
     270           0 :         if (can_forward.num_filter_ids == 0) {
     271           0 :             delete[] can_forward.filter_ids;
     272           0 :             can_forward.filter_ids = nullptr;
     273             :         }
     274           0 :         break;
     275             :     }
     276             :     }
     277           0 :     if (new_ids != nullptr) {
     278             :         // handle common case of no change
     279           0 :         if (num_new_ids == can_forward.num_filter_ids &&
     280           0 :             memcmp(new_ids, can_forward.filter_ids, num_new_ids*sizeof(uint16_t)) == 0) {
     281           0 :             delete[] new_ids;
     282             :         } else {
     283             :             // put the new list in place
     284           0 :             delete[] can_forward.filter_ids;
     285           0 :             can_forward.filter_ids = new_ids;
     286           0 :             can_forward.num_filter_ids = num_new_ids;
     287             :         }
     288             :     }
     289           0 : }
     290             : 
     291             : /*
     292             :   handler for CAN frames from the registered callback, sending frames
     293             :   out as CAN_FRAME or CANFD_FRAME messages
     294             :  */
     295           0 : void AP_MAVLinkCAN::can_frame_callback(uint8_t bus, const AP_HAL::CANFrame &frame, AP_HAL::CANIface::CanIOFlags flags)
     296             : {
     297             :     mavlink_channel_t chan;
     298             :     uint8_t system_id;
     299             :     uint8_t component_id;
     300             :     {
     301           0 :         WITH_SEMAPHORE(can_forward.sem);
     302           0 :         if (bus != can_forward.callback_bus) {
     303             :             // we are not registered for forwarding this bus, discard frame
     304           0 :             return;
     305             :         }
     306           0 :         if (can_forward.frame_counter++ == 100) {
     307             :             // check every 100 frames for disabling CAN_FRAME send
     308             :             // we stop sending after 5s if the client stops
     309             :             // sending MAV_CMD_CAN_FORWARD requests
     310           0 :             if (can_forward.callback_id != 0 &&
     311           0 :                 AP_HAL::millis() - can_forward.last_callback_enable_ms > 5000) {
     312           0 :                 hal.can[bus]->unregister_frame_callback(can_forward.callback_id);
     313           0 :                 can_forward.callback_id = 0;
     314           0 :                 return;
     315             :             }
     316           0 :             can_forward.frame_counter = 0;
     317             :         }
     318           0 :         if (can_forward.filter_ids != nullptr) {
     319             :             // work out ID of this frame
     320           0 :             uint16_t id = 0;
     321           0 :             if ((frame.id&0xff) != 0) {
     322             :                 // not anonymous
     323           0 :                 if (frame.id & 0x80) {
     324             :                     // service message
     325           0 :                     id = uint8_t(frame.id>>16);
     326             :                 } else {
     327             :                     // message frame
     328           0 :                     id = uint16_t(frame.id>>8);
     329             :                 }
     330             :             }
     331           0 :             if (!bisect_search_uint16(can_forward.filter_ids, can_forward.num_filter_ids, id)) {
     332           0 :                 return;
     333             :             }
     334             :         }
     335             :         // remeber destination while we hold the mutex
     336           0 :         chan = can_forward.chan;
     337           0 :         system_id = can_forward.system_id;
     338           0 :         component_id = can_forward.component_id;
     339           0 :     }
     340             : 
     341             :     // the rest is run without the can_forward.sem
     342           0 :     WITH_SEMAPHORE(comm_chan_lock(chan));
     343           0 :     const uint8_t data_len = AP_HAL::CANFrame::dlcToDataLength(frame.dlc);
     344             : #if HAL_CANFD_SUPPORTED
     345           0 :     if (frame.isCanFDFrame()) {
     346           0 :         if (HAVE_PAYLOAD_SPACE(chan, CANFD_FRAME)) {
     347           0 :             mavlink_msg_canfd_frame_send(chan, system_id, component_id,
     348           0 :                                          bus, data_len, frame.id, const_cast<uint8_t*>(frame.data));
     349             :         }
     350             :     } else
     351             : #endif
     352             :     {
     353           0 :         if (HAVE_PAYLOAD_SPACE(chan, CAN_FRAME)) {
     354           0 :             mavlink_msg_can_frame_send(chan, system_id, component_id,
     355           0 :                                        bus, data_len, frame.id, const_cast<uint8_t*>(frame.data));
     356             :         }
     357             :     }
     358           0 : }
     359             : 
     360             : #endif  // AP_MAVLINKCAN_ENABLED

Generated by: LCOV version 1.14