11import socket
22import struct
3+ import time
34
45# /**
56# * struct canfd_frame - CAN flexible data rate frame structure
2324CAN_MAX_DLEN = 8
2425CANFD_MAX_DLEN = 64
2526
27+ CAN_CONFIRM_FLAG = 0x800
28+ CAN_EFF_FLAG = 0x80000000
29+
2630CANFD_BRS = 0x01 # bit rate switch (second bitrate for payload data)
2731CANFD_FDF = 0x04 # mark CAN FD for dual use of struct canfd_frame
2832
3236
3337import typing
3438@typing .no_type_check # mypy struggles with macOS here...
35- def create_socketcan (interface :str , recv_buffer_size :int , fd : bool ) -> socket .socket :
39+ def create_socketcan (interface :str , recv_buffer_size :int ) -> socket .socket :
3640 # settings mostly from https://github.com/linux-can/can-utils/blob/master/candump.c
3741 socketcan = socket .socket (socket .AF_CAN , socket .SOCK_RAW , socket .CAN_RAW )
38- if fd :
39- socketcan .setsockopt (socket .SOL_CAN_RAW , socket .CAN_RAW_FD_FRAMES , 1 )
42+ socketcan .setblocking (False )
43+ socketcan .setsockopt (socket .SOL_CAN_RAW , socket .CAN_RAW_FD_FRAMES , 1 )
44+ socketcan .setsockopt (socket .SOL_CAN_RAW , socket .CAN_RAW_RECV_OWN_MSGS , 1 )
4045 socketcan .setsockopt (socket .SOL_SOCKET , socket .SO_RCVBUF , recv_buffer_size )
4146 # TODO: why is it always 2x the requested size?
4247 assert socketcan .getsockopt (socket .SOL_SOCKET , socket .SO_RCVBUF ) == recv_buffer_size * 2
@@ -47,50 +52,72 @@ def create_socketcan(interface:str, recv_buffer_size:int, fd:bool) -> socket.soc
4752
4853# Panda class substitute for socketcan device (to support using the uds/iso-tp/xcp/ccp library)
4954class SocketPanda ():
50- def __init__ (self , interface :str = "can0" , bus : int = 0 , fd : bool = False , recv_buffer_size :int = 212992 ) -> None :
55+ def __init__ (self , interface :str = "can0" , recv_buffer_size :int = 212992 ) -> None :
5156 self .interface = interface
52- self .bus = bus
53- self .fd = fd
54- self .flags = CANFD_BRS | CANFD_FDF if fd else 0
55- self .data_len = CANFD_MAX_DLEN if fd else CAN_MAX_DLEN
5657 self .recv_buffer_size = recv_buffer_size
57- self .socket = create_socketcan (interface , recv_buffer_size , fd )
58+ self .socket = create_socketcan (interface , recv_buffer_size )
5859
5960 def __del__ (self ):
6061 self .socket .close ()
6162
6263 def get_serial (self ) -> tuple [int , int ]:
63- return (0 , 0 ) # TODO: implemented in panda socketcan driver
64+ return (0 , 0 )
6465
6566 def get_version (self ) -> int :
66- return 0 # TODO: implemented in panda socketcan driver
67+ return 0
6768
6869 def can_clear (self , bus :int ) -> None :
69- # TODO: implemented in panda socketcan driver
7070 self .socket .close ()
71- self .socket = create_socketcan (self .interface , self .recv_buffer_size , self . fd )
71+ self .socket = create_socketcan (self .interface , self .recv_buffer_size )
7272
7373 def set_safety_mode (self , mode :int , param = 0 ) -> None :
74- pass # TODO: implemented in panda socketcan driver
74+ pass
7575
76- def has_obd (self ) -> bool :
77- return False # TODO: implemented in panda socketcan driver
76+ def can_send_many (self , arr , * , fd = False , timeout = 0 ) -> None :
77+ for msg in arr :
78+ self .can_send (* msg , fd = fd , timeout = timeout )
7879
79- def can_send (self , addr , dat , bus = 0 , timeout = 0 ) -> None :
80+ def can_send (self , addr , dat , bus , * , fd = False , timeout = 0 ) -> None :
81+ # Even if the CANFD_FDF flag is not set, the data still must be 8 bytes for classic CAN frames.
82+ data_len = CANFD_MAX_DLEN if fd else CAN_MAX_DLEN
8083 msg_len = len (dat )
81- msg_dat = dat .ljust (self .data_len , b'\x00 ' )
82- can_frame = struct .pack (CAN_HEADER_FMT , addr , msg_len , self .flags ) + msg_dat
83- self .socket .sendto (can_frame , (self .interface ,))
84+ msg_dat = dat .ljust (data_len , b'\x00 ' )
85+
86+ # Set extended ID flag
87+ if addr > 0x7ff :
88+ addr |= CAN_EFF_FLAG
89+
90+ # Set FD flags
91+ flags = CANFD_BRS | CANFD_FDF if fd else 0
92+
93+ can_frame = struct .pack (CAN_HEADER_FMT , addr , msg_len , flags ) + msg_dat
94+
95+ # Try to send until timeout. sendto might block if the TX buffer is full.
96+ # TX buffer size can also be adjusted through `ip link set can0 txqueuelen <size>` if needed
97+ start_t = time .monotonic ()
98+ while (time .monotonic () - start_t < (timeout / 1000 )) or (timeout == 0 ):
99+ try :
100+ self .socket .sendto (can_frame , (self .interface ,))
101+ break
102+ except (BlockingIOError , OSError ):
103+ continue
104+ else :
105+ raise TimeoutError
106+
84107
85108 def can_recv (self ) -> list [tuple [int , bytes , int ]]:
86109 msgs = list ()
87110 while True :
88111 try :
89- dat , _ = self .socket .recvfrom (self .recv_buffer_size , socket .MSG_DONTWAIT )
90- assert len (dat ) == CAN_HEADER_LEN + self .data_len , f"ERROR: received { len (dat )} bytes"
112+ dat , _ , msg_flags , _ = self .socket .recvmsg (self .recv_buffer_size )
113+ assert len (dat ) >= CAN_HEADER_LEN , f"ERROR: received { len (dat )} bytes"
114+
91115 can_id , msg_len , _ = struct .unpack (CAN_HEADER_FMT , dat [:CAN_HEADER_LEN ])
116+ assert len (dat ) >= CAN_HEADER_LEN + msg_len , f"ERROR: received { len (dat )} bytes, expected at least { CAN_HEADER_LEN + msg_len } bytes"
117+
92118 msg_dat = dat [CAN_HEADER_LEN :CAN_HEADER_LEN + msg_len ]
93- msgs .append ((can_id , msg_dat , self .bus ))
119+ bus = 128 if (msg_flags & CAN_CONFIRM_FLAG ) else 0
120+ msgs .append ((can_id , msg_dat , bus ))
94121 except BlockingIOError :
95122 break # buffered data exhausted
96123 return msgs
0 commit comments