Skip to content

Commit 613c653

Browse files
eldadcoolrahagalcymotive-eldad-sitbon
authored
CC-2174: modify CAN frame header structure to match updated struct ca… (#1851)
* CC-2174: modify CAN frame header structure to match updated struct can_frame from kernel * allow only valid fd message lengths * add remote message special case for dlc handling * fix pylint issues * verify non FD frame before assigning the len8 DLC * Fix formatting --------- Co-authored-by: Gal Rahamim <gal.rahamim@cymotive.com> Co-authored-by: Eldad Sitbon <eldad.sitbon@cymotive.com> Co-authored-by: rahagal <114643899+rahagal@users.noreply.github.com>
1 parent 319a9f2 commit 613c653

File tree

2 files changed

+105
-15
lines changed

2 files changed

+105
-15
lines changed

can/interfaces/socketcan/constants.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,18 @@
5353
SIOCGSTAMP = 0x8906
5454
EXTFLG = 0x0004
5555

56-
CANFD_BRS = 0x01
57-
CANFD_ESI = 0x02
56+
CANFD_BRS = 0x01 # bit rate switch (second bitrate for payload data)
57+
CANFD_ESI = 0x02 # error state indicator of the transmitting node
58+
CANFD_FDF = 0x04 # mark CAN FD for dual use of struct canfd_frame
59+
60+
# CAN payload length and DLC definitions according to ISO 11898-1
61+
CAN_MAX_DLC = 8
62+
CAN_MAX_RAW_DLC = 15
63+
CAN_MAX_DLEN = 8
64+
65+
# CAN FD payload length and DLC definitions according to ISO 11898-7
66+
CANFD_MAX_DLC = 15
67+
CANFD_MAX_DLEN = 64
5868

5969
CANFD_MTU = 72
6070

can/interfaces/socketcan/socketcan.py

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -139,23 +139,68 @@ def bcm_header_factory(
139139
# The 32bit can id is directly followed by the 8bit data link count
140140
# The data field is aligned on an 8 byte boundary, hence we add padding
141141
# which aligns the data field to an 8 byte boundary.
142-
CAN_FRAME_HEADER_STRUCT = struct.Struct("=IBB2x")
142+
CAN_FRAME_HEADER_STRUCT = struct.Struct("=IBB1xB")
143143

144144

145145
def build_can_frame(msg: Message) -> bytes:
146146
"""CAN frame packing/unpacking (see 'struct can_frame' in <linux/can.h>)
147147
/**
148-
* struct can_frame - basic CAN frame structure
149-
* @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
150-
* @can_dlc: the data length field of the CAN frame
151-
* @data: the CAN frame payload.
152-
*/
148+
* struct can_frame - Classical CAN frame structure (aka CAN 2.0B)
149+
* @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
150+
* @len: CAN frame payload length in byte (0 .. 8)
151+
* @can_dlc: deprecated name for CAN frame payload length in byte (0 .. 8)
152+
* @__pad: padding
153+
* @__res0: reserved / padding
154+
* @len8_dlc: optional DLC value (9 .. 15) at 8 byte payload length
155+
* len8_dlc contains values from 9 .. 15 when the payload length is
156+
* 8 bytes but the DLC value (see ISO 11898-1) is greater then 8.
157+
* CAN_CTRLMODE_CC_LEN8_DLC flag has to be enabled in CAN driver.
158+
* @data: CAN frame payload (up to 8 byte)
159+
*/
153160
struct can_frame {
154161
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
155-
__u8 can_dlc; /* data length code: 0 .. 8 */
156-
__u8 data[8] __attribute__((aligned(8)));
162+
union {
163+
/* CAN frame payload length in byte (0 .. CAN_MAX_DLEN)
164+
* was previously named can_dlc so we need to carry that
165+
* name for legacy support
166+
*/
167+
__u8 len;
168+
__u8 can_dlc; /* deprecated */
169+
} __attribute__((packed)); /* disable padding added in some ABIs */
170+
__u8 __pad; /* padding */
171+
__u8 __res0; /* reserved / padding */
172+
__u8 len8_dlc; /* optional DLC for 8 byte payload length (9 .. 15) */
173+
__u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
157174
};
158175
176+
/*
177+
* defined bits for canfd_frame.flags
178+
*
179+
* The use of struct canfd_frame implies the FD Frame (FDF) bit to
180+
* be set in the CAN frame bitstream on the wire. The FDF bit switch turns
181+
* the CAN controllers bitstream processor into the CAN FD mode which creates
182+
* two new options within the CAN FD frame specification:
183+
*
184+
* Bit Rate Switch - to indicate a second bitrate is/was used for the payload
185+
* Error State Indicator - represents the error state of the transmitting node
186+
*
187+
* As the CANFD_ESI bit is internally generated by the transmitting CAN
188+
* controller only the CANFD_BRS bit is relevant for real CAN controllers when
189+
* building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make
190+
* sense for virtual CAN interfaces to test applications with echoed frames.
191+
*
192+
* The struct can_frame and struct canfd_frame intentionally share the same
193+
* layout to be able to write CAN frame content into a CAN FD frame structure.
194+
* When this is done the former differentiation via CAN_MTU / CANFD_MTU gets
195+
* lost. CANFD_FDF allows programmers to mark CAN FD frames in the case of
196+
* using struct canfd_frame for mixed CAN / CAN FD content (dual use).
197+
* Since the introduction of CAN XL the CANFD_FDF flag is set in all CAN FD
198+
* frame structures provided by the CAN subsystem of the Linux kernel.
199+
*/
200+
#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */
201+
#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */
202+
#define CANFD_FDF 0x04 /* mark CAN FD for dual use of struct canfd_frame */
203+
159204
/**
160205
* struct canfd_frame - CAN flexible data rate frame structure
161206
* @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
@@ -175,14 +220,30 @@ def build_can_frame(msg: Message) -> bytes:
175220
};
176221
"""
177222
can_id = _compose_arbitration_id(msg)
223+
178224
flags = 0
225+
226+
# The socketcan code identify the received FD frame by the packet length.
227+
# So, padding to the data length is performed according to the message type (Classic / FD)
228+
if msg.is_fd:
229+
flags |= constants.CANFD_FDF
230+
max_len = constants.CANFD_MAX_DLEN
231+
else:
232+
max_len = constants.CAN_MAX_DLEN
233+
179234
if msg.bitrate_switch:
180235
flags |= constants.CANFD_BRS
181236
if msg.error_state_indicator:
182237
flags |= constants.CANFD_ESI
183-
max_len = 64 if msg.is_fd else 8
238+
184239
data = bytes(msg.data).ljust(max_len, b"\x00")
185-
return CAN_FRAME_HEADER_STRUCT.pack(can_id, msg.dlc, flags) + data
240+
241+
if msg.is_remote_frame:
242+
data_len = msg.dlc
243+
else:
244+
data_len = min(i for i in can.util.CAN_FD_DLC if i >= len(msg.data))
245+
header = CAN_FRAME_HEADER_STRUCT.pack(can_id, data_len, flags, msg.dlc)
246+
return header + data
186247

187248

188249
def build_bcm_header(
@@ -259,12 +320,31 @@ def build_bcm_update_header(can_id: int, msg_flags: int, nframes: int = 1) -> by
259320
)
260321

261322

323+
def is_frame_fd(frame: bytes):
324+
# According to the SocketCAN implementation the frame length
325+
# should indicate if the message is FD or not (not the flag value)
326+
return len(frame) == constants.CANFD_MTU
327+
328+
262329
def dissect_can_frame(frame: bytes) -> Tuple[int, int, int, bytes]:
263-
can_id, can_dlc, flags = CAN_FRAME_HEADER_STRUCT.unpack_from(frame)
264-
if len(frame) != constants.CANFD_MTU:
330+
can_id, data_len, flags, len8_dlc = CAN_FRAME_HEADER_STRUCT.unpack_from(frame)
331+
332+
if data_len not in can.util.CAN_FD_DLC:
333+
data_len = min(i for i in can.util.CAN_FD_DLC if i >= data_len)
334+
335+
can_dlc = data_len
336+
337+
if not is_frame_fd(frame):
265338
# Flags not valid in non-FD frames
266339
flags = 0
267-
return can_id, can_dlc, flags, frame[8 : 8 + can_dlc]
340+
341+
if (
342+
data_len == constants.CAN_MAX_DLEN
343+
and constants.CAN_MAX_DLEN < len8_dlc <= constants.CAN_MAX_RAW_DLC
344+
):
345+
can_dlc = len8_dlc
346+
347+
return can_id, can_dlc, flags, frame[8 : 8 + data_len]
268348

269349

270350
def create_bcm_socket(channel: str) -> socket.socket:

0 commit comments

Comments
 (0)