2 CAN UDP multicast server
5 #include "AP_Networking_Config.h"
7 #if AP_NETWORKING_CAN_MCAST_ENABLED
9 #include "AP_Networking.h"
11 #include <AP_HAL/utility/Socket.h>
12 #include <AP_HAL/CANIface.h>
13 #include <GCS_MAVLink/GCS.h>
14 #include <AP_Math/crc.h>
16 #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
18 #include <AP_HAL_ChibiOS/hwdef/common/stm32_util.h>
19 #include <AP_HAL_ChibiOS/CANIface.h>
22 #define MCAST_ADDRESS_BASE "239.65.82.0"
23 #define MCAST_PORT 57732U
24 #define MCAST_MAGIC 0x2934U
25 #define MCAST_FLAG_CANFD 0x0001
26 #define MCAST_MAX_PKT_LEN 74 // 64 byte data + 10 byte header
28 struct PACKED mcast_pkt
{
33 uint8_t data
[MCAST_MAX_PKT_LEN
-10];
36 #define MCAST_HDR_LENGTH offsetof(mcast_pkt, data)
38 extern const AP_HAL::HAL
& hal
;
40 #ifdef HAL_BOOTLOADER_BUILD
41 void AP_Networking_CAN::mcast_trampoline(void *ctx
)
43 auto *mcast
= (AP_Networking_CAN
*)ctx
;
44 mcast
->mcast_server();
46 extern ChibiOS::CANIface can_iface
[HAL_NUM_CAN_IFACES
];
47 extern void thread_sleep_us(uint32_t us
);
48 #endif // HAL_BOOTLOADER_BUILD
51 get CAN interface for a bus
53 AP_HAL::CANIface
*AP_Networking_CAN::get_caniface(uint8_t bus
) const
55 #ifdef HAL_BOOTLOADER_BUILD
56 return &can_iface
[bus
];
63 start the CAN multicast server
65 void AP_Networking_CAN::start(const uint8_t _bus_mask
)
67 const uint32_t stack_size
= 8192;
70 #ifdef HAL_BOOTLOADER_BUILD
71 thread_create_alloc(THD_WORKING_AREA_SIZE(stack_size
),
77 hal
.scheduler
->thread_create(FUNCTOR_BIND_MEMBER(&AP_Networking_CAN::mcast_server
, void),
79 stack_size
, AP_HAL::Scheduler::PRIORITY_CAN
, -1);
84 main thread for CAN multicast server
86 void AP_Networking_CAN::mcast_server(void)
88 #ifndef HAL_BOOTLOADER_BUILD
89 while (!hal
.scheduler
->is_system_initialized()) {
90 hal
.scheduler
->delay(100);
93 GCS_SEND_TEXT(MAV_SEVERITY_INFO
, "CAN_MCAST: starting");
95 ObjectBuffer
<AP_HAL::CANFrame
> *frame_buffers
[HAL_NUM_CAN_IFACES
] {};
97 for (uint8_t bus
=0; bus
<HAL_NUM_CAN_IFACES
; bus
++) {
98 auto *cbus
= get_caniface(bus
);
99 if (cbus
== nullptr) {
102 if (bus_mask
& (1U<<bus
)) {
103 mcast_sockets
[bus
] = NEW_NOTHROW
SocketAPM(true);
104 if (mcast_sockets
[bus
] == nullptr) {
105 GCS_SEND_TEXT(MAV_SEVERITY_ERROR
, "CAN_MCAST[%u]: failed to create socket", unsigned(bus
));
109 char address
[] = MCAST_ADDRESS_BASE
;
110 const uint32_t buffer_size
= 20; // good for fw upload
111 uint8_t callback_id
= 0;
113 address
[strlen(address
)-1] = '0' + bus
;
114 if (!mcast_sockets
[bus
]->connect(address
, MCAST_PORT
)) {
115 GCS_SEND_TEXT(MAV_SEVERITY_ERROR
, "CAN_MCAST[%u]: failed to connect", unsigned(bus
));
119 if (!cbus
->register_frame_callback(
120 FUNCTOR_BIND_MEMBER(&AP_Networking_CAN::can_frame_callback
, void, uint8_t, const AP_HAL::CANFrame
&, AP_HAL::CANIface::CanIOFlags
),
122 GCS_SEND_TEXT(MAV_SEVERITY_ERROR
, "CAN_MCAST[%u]: failed to register", unsigned(bus
));
126 #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
127 // tell the ethernet interface that we want to receive all
129 ETH
->MACPFR
|= ETH_MACPFR_PM
;
132 frame_buffers
[bus
] = NEW_NOTHROW ObjectBuffer
<AP_HAL::CANFrame
>(buffer_size
);
133 if (frame_buffers
[bus
] == nullptr) {
134 GCS_SEND_TEXT(MAV_SEVERITY_ERROR
, "CAN_MCAST[%u]: failed to allocate buffers", unsigned(bus
));
140 delete mcast_sockets
[bus
];
141 mcast_sockets
[bus
] = nullptr;
147 const uint32_t delay_us
= 100; // limit to 10k packets/s
148 #ifndef HAL_BOOTLOADER_BUILD
149 hal
.scheduler
->delay_microseconds(delay_us
);
151 thread_sleep_us(delay_us
);
153 for (uint8_t bus
=0; bus
<HAL_NUM_CAN_IFACES
; bus
++) {
154 if (mcast_sockets
[bus
] == nullptr) {
158 struct mcast_pkt pkt
;
159 const ssize_t ret
= mcast_sockets
[bus
]->recv((void*)&pkt
, sizeof(pkt
), 0);
160 if (ret
> MCAST_HDR_LENGTH
&& ret
<= sizeof(pkt
)) {
161 const uint8_t data_len
= ret
- MCAST_HDR_LENGTH
;
162 bool is_canfd
= false;
163 #if HAL_CANFD_SUPPORTED
164 is_canfd
= (pkt
.flags
& MCAST_FLAG_CANFD
) != 0;
166 if (pkt
.magic
!= MCAST_MAGIC
) {
169 const auto crc
= crc16_ccitt((uint8_t*)&pkt
.flags
, ret
- offsetof(mcast_pkt
,flags
), 0xFFFFU
);
170 if (pkt
.crc
!= crc
) {
175 frame_buffers
[bus
]->push(AP_HAL::CANFrame(pkt
.message_id
, pkt
.data
, data_len
, is_canfd
));
181 AP_HAL::CANFrame frame
;
182 const uint16_t timeout_us
= 2000;
183 #if AP_NETWORKING_CAN_MCAST_BRIDGING_ENABLED
184 const bool bridged
= AP::network().is_can_mcast_ep_bridged(bus
);
187 while (frame_buffers
[bus
]->peek(frame
)) {
188 auto *cbus
= get_caniface(bus
);
189 if (cbus
== nullptr) {
192 #if AP_NETWORKING_CAN_MCAST_BRIDGING_ENABLED
194 auto retcode
= cbus
->send(frame
, AP_HAL::micros64() + timeout_us
,
195 AP_HAL::CANIface::IsForwardedFrame
);
202 // only call the AP_HAL::CANIface send if we are not in bridged mode
203 cbus
->AP_HAL::CANIface::send(frame
, AP_HAL::micros64() + timeout_us
,
204 AP_HAL::CANIface::IsForwardedFrame
);
207 // we either sent it or there was an error, either way we discard the frame
208 frame_buffers
[bus
]->pop();
215 handler for CAN frames from the registered callback, sending frames
218 void AP_Networking_CAN::can_frame_callback(uint8_t bus
, const AP_HAL::CANFrame
&frame
, AP_HAL::CANIface::CanIOFlags flags
)
220 if (bus
>= HAL_NUM_CAN_IFACES
|| mcast_sockets
[bus
] == nullptr) {
224 #if AP_NETWORKING_CAN_MCAST_BRIDGING_ENABLED
225 // check if bridged mode is enabled
226 const bool bridged
= AP::network().is_can_mcast_ep_bridged(bus
);
228 // never bridge in bootloader, as we can cause loops if multiple
229 // bootloaders with mcast are running on the same network and CAN Bus
230 const bool bridged
= false;
233 if ((flags
& AP_HAL::CANIface::IsForwardedFrame
) && !bridged
) {
234 // we don't forward frames that we received from the multicast
235 // server if not in bridged mode
239 struct mcast_pkt pkt
{};
240 pkt
.magic
= MCAST_MAGIC
;
242 #if HAL_CANFD_SUPPORTED
243 if (frame
.isCanFDFrame()) {
244 pkt
.flags
|= MCAST_FLAG_CANFD
;
247 pkt
.message_id
= frame
.id
;
249 const uint8_t data_length
= AP_HAL::CANFrame::dlcToDataLength(frame
.dlc
);
251 memcpy(pkt
.data
, frame
.data
, data_length
);
252 // 6 is the size of the flags and message_id, ie header data after crc
253 pkt
.crc
= crc16_ccitt((uint8_t*)&pkt
.flags
, data_length
+6, 0xFFFFU
);
255 mcast_sockets
[bus
]->send((void*)&pkt
, data_length
+MCAST_HDR_LENGTH
);
258 #endif // AP_NETWORKING_ENABLED