@@ -135,7 +135,7 @@ sendMsg(const modm::can::Message& message)
135
135
{
136
136
using namespace modm::platform;
137
137
138
- if (!Fdcan{{ id }}::isReadyToSend ()) {
138
+ if (isHardwareTxQueueFull ()) {
139
139
return false;
140
140
}
141
141
@@ -215,20 +215,25 @@ modm::platform::Fdcan{{ id }}::initializeWithPrescaler(
215
215
MODM_ISR({{ reg }}_IT0)
216
216
{
217
217
%% if options["buffer.tx"] > 0
218
- if (txQueue.isNotEmpty()) {
219
- sendMsg(txQueue.get());
220
- txQueue.pop();
218
+ if ({{ reg }}->IR & FDCAN_IR_TC) {
219
+ {{ reg }}->IR = FDCAN_IR_TC;
220
+ if (txQueue.isNotEmpty()) {
221
+ const bool success = sendMsg(txQueue.get());
222
+ if (success) {
223
+ txQueue.pop();
224
+ }
225
+ }
221
226
}
222
227
%% endif
223
228
224
- const auto callback = modm::platform::Fdcan {{ id }}::getErrorInterruptCallback( );
225
- if (callback ) {
226
- const bool hasErrorInterrupt = ( {{ reg }}->IR & (FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP) );
227
- if (hasErrorInterrupt ) {
229
+ const bool hasErrorInterrupt = ( {{ reg }}->IR & (FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP) );
230
+ if (hasErrorInterrupt ) {
231
+ const auto callback = modm::platform::Fdcan {{ id }}::getErrorInterruptCallback( );
232
+ if (callback ) {
228
233
callback();
229
234
}
235
+ {{ reg }}->IR = FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP;
230
236
}
231
- {{ reg }}->IR = FDCAN_IR_TC | FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP;
232
237
}
233
238
234
239
@@ -258,7 +263,7 @@ MODM_ISR({{ reg }}_IT1)
258
263
"CAN receive software buffer full, not reading new message(s)!");
259
264
// disable rx ISR until we read data from the rxQueue (IST for tx remains active)
260
265
// The interrupt remains unacknowledged, so it fires again after the read.
261
- {{ reg }}->ILE = FDCAN_ILE_EINT0 ;
266
+ {{ reg }}->ILE &= ~FDCAN_ILE_EINT1 ;
262
267
} else {
263
268
{{ reg }}->IR = FDCAN_IR_RF0N | FDCAN_IR_RF1N; // acknowledge interrupt flags
264
269
}
@@ -402,10 +407,27 @@ modm::platform::Fdcan{{ id }}::isReadyToSend()
402
407
%% endif
403
408
}
404
409
405
-
406
410
bool
407
411
modm::platform::Fdcan{{ id }}::sendMessage(const can::Message& message)
408
412
{
413
+ %% if options["buffer.tx"] > 0
414
+ /* Disable CAN interrupts to prevent race condition:
415
+ * sendMsg() could be called concurrently from the TX interrupt and
416
+ * simultaneously access the same TX buffer.
417
+ * isHardwareTxQueueFull() must be checked while interrupts are off to
418
+ * prevent a "time-of-check to time-of-use" bug. */
419
+ struct Lock
420
+ {
421
+ const uint32_t flags;
422
+ /* An edge case could occur where the RX interrupt gets disabled
423
+ * in the RX handler when the queue is full between reading ILE and
424
+ * writing ILE = 0. This will invoke the RX handler once
425
+ * after sendMessage() but not cause any further issues. */
426
+ Lock() : flags({{ reg }}->ILE) { {{ reg }}->ILE = 0; }
427
+ ~Lock() { {{ reg }}->ILE = flags; }
428
+ } lock;
429
+ %% endif
430
+
409
431
if (isHardwareTxQueueFull()) {
410
432
%% if options["buffer.tx"] > 0
411
433
if (txQueue.isFull()) {
0 commit comments