Skip to content

Commit 0e3812d

Browse files
karldinghardbyte
authored andcommitted
Backport socketcan fix: Read the BCM status before creating a Task
Currently bus.send_periodic blindly wraps the BCM socket API, and sends a BCM operation with the TX_SETUP opcode. However, the semantics that the kernel driver provides are an "upsert". As such, when one attempts to create two Tasks with the same CAN Arbitration ID, it ends up silently modifying the periodic messages, which is contrary to what our API implies. This fixes the problem by first sending a TX_READ opcode with the CAN Arbitration ID that we wish to create. The kernel driver will return -EINVAL if a periodic transmission with the given Arbitration ID does not exist. If this is the case, then we can continue creating the Task, otherwise we error and notify the user. hardbyte#605
1 parent 250d290 commit 0e3812d

2 files changed

Lines changed: 36 additions & 4 deletions

File tree

can/interfaces/socketcan/constants.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
CAN_EFF_FLAG = 0x80000000
1010

1111
# BCM opcodes
12-
CAN_BCM_TX_SETUP = 1
13-
CAN_BCM_TX_DELETE = 2
12+
CAN_BCM_TX_SETUP = 1
13+
CAN_BCM_TX_DELETE = 2
14+
CAN_BCM_TX_READ = 3
1415

1516
# BCM flags
1617
SETTIMER = 0x0001

can/interfaces/socketcan/socketcan.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,39 @@ def _tx_setup(self, message):
346346
count = 0
347347
ival1 = 0
348348
ival2 = self.period
349-
header = build_bcm_transmit_header(self.can_id_with_flags, count, ival1,
350-
ival2, self.flags)
349+
350+
# First do a TX_READ before creating a new task, and check if we get
351+
# EINVAL. If so, then we are referring to a CAN message with the same
352+
# ID
353+
check_header = build_bcm_header(
354+
opcode=CAN_BCM_TX_READ,
355+
flags=0,
356+
count=0,
357+
ival1_seconds=0,
358+
ival1_usec=0,
359+
ival2_seconds=0,
360+
ival2_usec=0,
361+
can_id=self.can_id_with_flags,
362+
nframes=0,
363+
)
364+
try:
365+
self.bcm_socket.send(check_header)
366+
except OSError as e:
367+
assert e.errno == errno.EINVAL
368+
else:
369+
raise ValueError(
370+
"A periodic Task for Arbitration ID {} has already been created".format(
371+
message.arbitration_id
372+
)
373+
)
374+
375+
header = build_bcm_transmit_header(
376+
self.can_id_with_flags,
377+
count,
378+
ival1,
379+
ival2,
380+
self.flags
381+
)
351382
frame = build_can_frame(message)
352383
log.debug("Sending BCM command")
353384
send_bcm(self.bcm_socket, header + frame)

0 commit comments

Comments
 (0)