@@ -139,23 +139,68 @@ def bcm_header_factory(
139
139
# The 32bit can id is directly followed by the 8bit data link count
140
140
# The data field is aligned on an 8 byte boundary, hence we add padding
141
141
# 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 " )
143
143
144
144
145
145
def build_can_frame (msg : Message ) -> bytes :
146
146
"""CAN frame packing/unpacking (see 'struct can_frame' in <linux/can.h>)
147
147
/**
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
+ */
153
160
struct can_frame {
154
161
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)));
157
174
};
158
175
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
+
159
204
/**
160
205
* struct canfd_frame - CAN flexible data rate frame structure
161
206
* @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:
175
220
};
176
221
"""
177
222
can_id = _compose_arbitration_id (msg )
223
+
178
224
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
+
179
234
if msg .bitrate_switch :
180
235
flags |= constants .CANFD_BRS
181
236
if msg .error_state_indicator :
182
237
flags |= constants .CANFD_ESI
183
- max_len = 64 if msg . is_fd else 8
238
+
184
239
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
186
247
187
248
188
249
def build_bcm_header (
@@ -259,12 +320,31 @@ def build_bcm_update_header(can_id: int, msg_flags: int, nframes: int = 1) -> by
259
320
)
260
321
261
322
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
+
262
329
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 ):
265
338
# Flags not valid in non-FD frames
266
339
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 ]
268
348
269
349
270
350
def create_bcm_socket (channel : str ) -> socket .socket :
0 commit comments