@@ -135,7 +135,7 @@ sendMsg(const modm::can::Message& message)
135135{
136136 using namespace modm::platform;
137137
138- if (!Fdcan{{ id }}::isReadyToSend ()) {
138+ if (isHardwareTxQueueFull ()) {
139139 return false;
140140 }
141141
@@ -215,20 +215,25 @@ modm::platform::Fdcan{{ id }}::initializeWithPrescaler(
215215MODM_ISR({{ reg }}_IT0)
216216{
217217%% 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+ }
221226 }
222227%% endif
223228
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 ) {
228233 callback();
229234 }
235+ {{ reg }}->IR = FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP;
230236 }
231- {{ reg }}->IR = FDCAN_IR_TC | FDCAN_IR_BO | FDCAN_IR_EW | FDCAN_IR_EP;
232237}
233238
234239
@@ -258,7 +263,7 @@ MODM_ISR({{ reg }}_IT1)
258263 "CAN receive software buffer full, not reading new message(s)!");
259264 // disable rx ISR until we read data from the rxQueue (IST for tx remains active)
260265 // The interrupt remains unacknowledged, so it fires again after the read.
261- {{ reg }}->ILE = FDCAN_ILE_EINT0 ;
266+ {{ reg }}->ILE &= ~FDCAN_ILE_EINT1 ;
262267 } else {
263268 {{ reg }}->IR = FDCAN_IR_RF0N | FDCAN_IR_RF1N; // acknowledge interrupt flags
264269 }
@@ -402,10 +407,27 @@ modm::platform::Fdcan{{ id }}::isReadyToSend()
402407%% endif
403408}
404409
405-
406410bool
407411modm::platform::Fdcan{{ id }}::sendMessage(const can::Message& message)
408412{
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+
409431 if (isHardwareTxQueueFull()) {
410432%% if options["buffer.tx"] > 0
411433 if (txQueue.isFull()) {
0 commit comments