1"""
2Controller Area Network, ISO 11898-1, see https://en.wikipedia.org/wiki/CAN_bus
3SocketCAN message format handling
4
5API examples:
6https://github.com/rhyttr/SocketCAN/tree/3c46872d9af0885b42526b70853400c6d94b7c54/can-utils
7"""
8import logging
9import sys
10
11from pypacker import pypacker
12
13logger = logging.getLogger("pypacker")
14
15module_this = sys.modules[__name__]
16
17
18"""
19OBDII modes:
20
2101 	Show current data
2202 	Show freeze frame data
2303 	Show stored Diagnostic Trouble Codes
2404 	Clear Diagnostic Trouble Codes and stored values
2505 	Test results, oxygen sensor monitoring (non CAN only)
2606 	Test results, other component/system monitoring
2707 	Show pending Diagnostic Trouble Codes (detected during current or last driving cycle)
2808 	Control operation of on-board component/system
2909 	Request vehicle information
300A 	Permanent Diagnostic Trouble Codes (DTCs) (Cleared DTCs)
31"""
32
33
34OBD2_MODE_01_PIDS = {
35	0x00	: "PIDS_SUPPORTED_1_20",
36	0x01	: "MONITOR_STATUS",
37	0x02	: "FREEZE_DTC",
38	0x03	: "FUEL_SYSTEM_STATUS",
39	0x04	: "CALCULATED_ENGINE_LOAD",
40	0x05	: "ENGINE_COOLANT_TEMP",
41	0x06	: "SHORT_TERM_FUEL_BANK1",
42	0x07	: "LONG_TERM_FUEL_BANK1",
43	0x08	: "SHORT_TERM_FUEL_BANK2",
44	0x09	: "LONG_TERM_FUEK_BANK2",
45	0x0A	: "FUELPRESSURE",
46	0x0B	: "INTAKE_MANIFOLD_PRESSURE",
47	0x0C	: "ENGINE_RPM",
48	0x0D	: "VEHICLE_SPEED",
49	0x0E	: "TIMING_ADVANCE",
50	0x0F	: "INTAKGE_AIR_TEMP",
51	0x10	: "MAF_AIR_FLOW",
52	0x11	: "THROTTLE_POS",
53	0x12	: "COMMANDED_SECONDARY_AIR_STATUS",
54	0x13	: "OXYGEN_SENSORS_PRESENT",
55	0x14	: "OXYGEN_SENSOR1",
56	0x15	: "OXYGEN_SENSOR2",
57	0x16	: "OXYGEN_SENSOR3",
58	0x17	: "OXYGEN_SENSOR4",
59	0x18	: "OXYGEN_SENSOR5",
60	0x19	: "OXYGEN_SENSOR6",
61	0x1A	: "OXYGEN_SENSOR7",
62	0x1B	: "OXYGEN_SENSOR8",
63	0x1C	: "OBD_STANDARDS_",
64	0x1D	: "OXYGEN_SENSORS",
65	0x1E	: "AUXILIARY_INPUT_STATUS",
66	0x1F	: "RUNTIME_SINCE_ENGINE",
67	0x20	: "PIDS_SUPPORTED_21_40",
68	0x21	: "DISTANCE_TRAVELED_WITH_MALFUNCTION",
69	0x22	: "FUEL_RAIL_PRESSURE",
70	0x23	: "FUEL_RAIL_GAUGE_PRESSURE",
71	0x24	: "OXYGEN_SENSOR1",
72	0x25	: "OXYGEN_SENSOR2",
73	0x26	: "OXYGEN_SENSOR3",
74	0x27	: "OXYGEN_SENSOR4",
75	0x28	: "OXYGEN_SENSOR5",
76	0x29	: "OXYGEN_SENSOR6",
77	0x2A	: "OXYGEN_SENSOR7",
78	0x2B	: "OXYGEN_SENSOR8",
79	0x2C	: "COMMANDED_EGR",
80	0x2D	: "EGR_ERROR",
81	0x2E	: "COMMANDED_EVAPORATIVE_PURGE",
82	0x2F	: "FUEL_TANK_LEVEL_INPUT",
83	0x30	: "WARMUPS_SINCE_CODES_CLEARED",
84	0x31	: "DISTANCE_TRAVELED_SINCE_CODES_CLEARED",
85	0x32	: "EVAP_SYSTEM_VAPOR_PRESSURE",
86	0x33	: "ABSOLUTE_BAROMETIC_PRESSURE",
87	0x34	: "OXYGEN_SENSOR1_FUELAIR",
88	0x35	: "OXYGEN_SENSOR2_FUELAIR",
89	0x36	: "OXYGEN_SENSOR3_FUELAIR",
90	0x37	: "OXYGEN_SENSOR4_FUELAIR",
91	0x38	: "OXYGEN_SENSOR5_FUELAIR",
92	0x39	: "OXYGEN_SENSOR6_FUELAIR",
93	0x3A	: "OXYGEN_SENSOR7_FUELAIR",
94	0x3B	: "OXYGEN_SENSOR8_FUELAIR",
95	0x3C	: "CATALYST_TEMP_B1S1",
96	0x3D	: "CATALYST_TEMP_B2S1",
97	0x3E	: "CATALYST_TEMP_B1S2",
98	0x3F	: "CATALYST_TEMP_B2S2",
99	0x40	: "PIDS_SUPPORTED_41_60",
100	0x41	: "MONITOR_STATUS_",
101	0x42	: "CTRL_MODULE_VOLTAGE",
102	0x43	: "ABS_LOAD_VALUE",
103	0x44	: "FUEL_AIR_COMMANDED",
104	0x45	: "RELATIVE_THROTTLE_POS",
105	0x46	: "AMBIENT_AIR_TEMP",
106	0x47	: "ABSOLUTE_THROTTLE_POS_B",
107	0x48	: "ABSOLUTE_THROTTLE_POS_C",
108	0x49	: "ABSOLUTE_THROTTLE_POS_D",
109	0x4A	: "ABSOLUTE_THROTTLE_POS_E",
110	0x4B	: "ABSOLUTE_THROTTLE_POS_F",
111	0x4C	: "COMMANDED_THROTTLE_ACTUATOR",
112	0x4D	: "TIME_RUN_WITH_MIL",
113	0x4E	: "TIME_SINCE_TROUBLE_CODES_CLEARED",
114	0x4F	: "MAX_FUEL_AIR_EQ_RATIO",
115	0x50	: "MAX_AIR_FLOW",
116	0x51	: "FUEL_TYPE",
117	0x52	: "ETHANOL_FUEL",
118	0x53	: "ABS_EVAP_SYSTEM_VAPOR_PRESSURE",
119	0x54	: "EVAP_SYSTEM_VAPOR_PRESSURE",
120	0x55	: "SHORT_TERM_SECONDARY_OXYGEN_1_3",
121	0x56	: "LONG_TERM_SECONDARY_OXYGEN_1_3",
122	0x57	: "SHORT_TERM_SECONDARY_OXYGEN_2_4",
123	0x58	: "LONG_TERM_SECONDARY_OXYGEN_2_4",
124	0x59	: "FUEL_RAIL_ABS_PRESSURE",
125	0x5A	: "RELATIVE_ACCELERATOR",
126	0x5B	: "HYBRID_BATTERY_PACK",
127	0x5C	: "ENGINE_OIL_TEMP",
128	0x5D	: "FUEL_INJECTION_TIMING",
129	0x5E	: "ENGINE_FUEL_RATE",
130	0x5F	: "EMISSION_REQUIREMENTS",
131	0x60	: "PIDS_SUPPORTED_61_80",
132	0x61	: "DEMAND_ENGINE",
133	0x62	: "ACTUAL_ENGINE",
134	0x63	: "ENGINE_REF_TORQUE",
135	0x64	: "ENGINE_PERCENT_TORQUE",
136	0x65	: "AUXILIARY_INPUT",
137	0x66	: "MASS_AIR_FLOW_SENSOR",
138	0x67	: "ENGINE_COOLANT_TEMP",
139	0x68	: "INTAKE_AIR_TEMP_SENSOR",
140	0x69	: "COMMANDED_EGR_ERROR",
141	0x6A	: "COMMANDED_DIESEL_INTAKE",
142	0x6B	: "EXHAUST_GAS_RECIRCULATION",
143	0x6C	: "COMMANDED_THROTTLE_ACTUATOR",
144	0x6D	: "FUEL_PRESSURE_CONTROL_SYSTEM",
145	0x6E	: "INJECTION_PRESSURE_CONTROL_SYSTEM",
146	0x6F	: "TURBOCHARGER_COMPRESSOR",
147	0x70	: "BOOST_PRESSURE",
148	0x71	: "VARIABLE_GEOMETRY",
149	0x72	: "WASTEGATE_CONTROL",
150	0x73	: "EXHAUST_PRESSURE",
151	0x74	: "TURBOCHARGER_RPM",
152	0x75	: "TURBOCHARGER_TEMP",
153	0x76	: "TURBOCHARGER_TEMP",
154	0x77	: "CHARGER_AIR_COOLER_TEMP",
155	0x78	: "EXHAUST_GAS_TEMP",
156	0x79	: "EXHAUST_GAS_TEMP",
157	0x7A	: "DIESEL_PARTICULATE_FILTER",
158	0x7B	: "DIESEL_PARTICULATE_FILTER",
159	0x7C	: "DIESEL_PARTICULATE_FILTER_TEMP",
160	0x7D	: "NOX_CTRL_AREA",
161	0x7E	: "PM_NTE_CTLR_AREA",
162	0x7F	: "ENGINE_RUN_TIME",
163	0x80	: "PIDS_SUPPORTED_81_A0",
164	0x81	: "ENGINE_RUNTIME_AUXILIARY",
165	0x82	: "ENGINE_RUNTIME_AUXILIARY",
166	0x83	: "NOX_SENSOR",
167	0x84	: "MANIFOLD_SURFACE_TEMP",
168	0x85	: "NOX_REAGENT_SYSTEM",
169	0x86	: "PARTICULATE_MATTER_SENSOR",
170	0x87	: "INTAKE_MANIFOLD_PRESSURE",
171	0xA0	: "PIDS_SUPPORTED_A1_C0",
172	0xC0	: "PIDS_SUPPORTED_C1_E0",
173	0xC3	: "?",
174	0xC4	: "?"
175}
176
177OBD2_MODE_02_PIDS = {
178	0x02	: "DTC_FREEZE_STORED",
179}
180
181OBD2_MODE_05_PIDS = {
182	0x0100	: "OBD_MONITOR_IDS",
183	0x0101	: "O2_SENSOR_1_1",
184	0x0102	: "O2_SENSOR_1_2",
185	0x0103	: "O2_SENSOR_1_3",
186	0x0104	: "O2_SENSOR_1_4",
187	0x0105	: "O2_SENSOR_2_1",
188	0x0106	: "O2_SENSOR_2_2",
189	0x0107	: "O2_SENSOR_2_3",
190	0x0108	: "O2_SENSOR_2_4",
191	0x0109	: "O2_SENSOR_3_1",
192	0x010A	: "O2_SENSOR_3_2",
193	0x010B	: "O2_SENSOR_3_3",
194	0x010C	: "O2_SENSOR_3_4",
195	0x010D	: "O2_SENSOR_4_1",
196	0x010E	: "O2_SENSOR_4_2",
197	0x010F	: "O2_SENSOR_4_3",
198	0x0110	: "O2_SENSOR_4_4",
199	0x0201	: "O2_SENSOR_1_1",
200	0x0202	: "O2_SENSOR_1_2",
201	0x0203	: "O2_SENSOR_1_3",
202	0x0204	: "O2_SENSOR_1_4",
203	0x0205	: "O2_SENSOR_2_1",
204	0x0206	: "O2_SENSOR_2_2",
205	0x0207	: "O2_SENSOR_2_3",
206	0x0208	: "O2_SENSOR_2_4",
207	0x0209	: "O2_SENSOR_3_1",
208	0x020A	: "O2_SENSOR_3_2",
209	0x020B	: "O2_SENSOR_3_3",
210	0x020C	: "O2_SENSOR_3_4",
211	0x020D	: "O2_SENSOR_4_1",
212	0x020E	: "O2_SENSOR_4_2",
213	0x020F	: "O2_SENSOR_4_3",
214	0x0210	: "O2_SENSOR_4_4"
215}
216
217OBD2_MODE_0A_PIDS = {
218	0x00	: "MODE_9_SUPPORTED_PIDS_1_20",
219	0x01	: "VIN_MESSAGE_COUNT",
220	0x02	: "VIN",
221	0x03	: "CALIBRATION_ID_MESSAGE_COUNT",
222	0x04	: "CALLIBRATION_ID",
223	0x05	: "CVN_MESSAGE_COUNT",
224	0x06	: "CVN",
225	0x07	: "PERFORMANCE_TRACKING_MESSAGE_COUNT",
226	0x08	: "PERFORMANCE_TRACKING",
227	0x09	: "ECU_NAME_MESSAGE_COUNT",
228	0x0A	: "ECU_NAME",
229	0x0B	: "PERFORMANCE_TRACKING_COMPRESSION"
230}
231
232OBD2_MODE_DESCR = {
233	0x01	: "OBD2_MODE_SHOW_CURRENT_DATA1",
234	0x02	: "OBD2_MODE_SHOW_CURRENT_DATA2",
235	0x03	: "OBD2_MODE_SHOW_STORED_TROUBLECODES",
236	0x04	: "OBD2_MODE_CLEAR_DIAGNOSTIC_TROUBLECODES",
237	0x05	: "OBD2_MODE_TEST_RESULTS_OXYGEN",
238	0x06	: "OBD2_MODE_TEST_RESULTS_OTHER",
239	0x07	: "OBD2_MODE_SHOW_PENDING_DIAGNOSTIC_TROUBLECODES",
240	0x08	: "OBD2_MODE_CONTROL_OPERATION",
241	0x09	: "OBD2_MODE_REQUEST_VEHICLE_INFO",
242	0x0A	: "OBD2_MODE_PERMANENT_DIAGNOSTIC_TROUBLECODES"
243}
244
245for obdid, name in OBD2_MODE_DESCR.items():
246	setattr(module_this, name, obdid)
247
248# Diagnostic and Communications Management
249UDS_SID_DESCR = {
250	0x10	: "UDS_SID_DIAGNOSTIC_SESSION_CONTORL",
251	0x11	: "UDS_SID_ECU_RESET",
252	0x12	: "UDS_SID_GMLAN_READ_FAILURE_RECORD",
253	0x14	: "UDS_SID_CLEAR_DIAGNOSTIC_INFORMATION",
254	0x19	: "UDS_SID_READ_DTC_INFORMATION",
255	0x1A	: "UDS_SID_GMLAN_READ_DIAGNOSTIC_ID",
256	0x20	: "UDS_SID_RETURN_TO_NORMAL",
257	0x22	: "UDS_SID_READ_DATA_BY_ID",
258	0x23	: "UDS_SID_READ_MEMORY_BY_ADDRESS",
259	0x24	: "UDS_SID_READ_SCALING_DATA_BY_ID",
260	0x27	: "UDS_SID_SECURITY_ACCESS",
261	0x28	: "UDS_SID_COMMUNICATION_CONTROL",
262	0x2A	: "UDS_SID_READ_DATA_BY_PERIODIC_ID",
263	0x2C	: "UDS_SID_DYNAMICALLY_DEFINE_DATA_ID",
264	0x2D	: "UDS_SID_DEFINE_PID_BY_MEMORY_ADDRESS",
265	0x2E	: "UDS_SID_WRITE_DATA_BY_ID",
266	0x2F	: "UDS_SID_IO_CONTORL_BY_ID",
267	0x31	: "UDS_SID_ROUTINE_CONTROL",
268	0x34	: "UDS_SID_REQUEST_DOWNLOAD",
269	0x35	: "UDS_SID_REQUEST_UPLOAD",
270	0x36	: "UDS_SID_TRANSFER_DATA",
271	0x37	: "UDS_SID_REQUEST_TANSFER_EXIT",
272	0x38	: "UDS_SID_REQUEST_FILE_TRANSFER",
273	0x3B	: "UDS_SID_GMLAN_WRITE_DID",
274	0x3D	: "UDS_SID_WRITE_MEMORY_BY_ADDRESS",
275	0x3E	: "UDS_SID_TESTER_PRESENT",
276	0x83	: "UDS_SID_ACCESS_TIMING_PARAMETER",
277	0x84	: "UDS_SID_SECURED_TRANSMISSION",
278	0x85	: "UDS_SID_CONTORL_DTC_SETTING",
279	0x86	: "UDS_SID_RESPONSE_ON_EVENT",
280	0x87	: "UDS_SID_LINK_CONTORL",
281	0xA2	: "UDS_SID_GMLAN_REPORT_PROGRAMMING_STATE",
282	0xA5	: "UDS_SID_GMLAN_ENTER_PROGRAMMING_MODE",
283	0xA9	: "UDS_SID_GMLAN_CHECK_CODES",
284	0xAA	: "UDS_SID_GMLAN_READ_DPID",
285	0xAE	: "UDS_SID_GMLAN_DEVICE_CONTROL",
286}
287
288for udsid, name in UDS_SID_DESCR.items():
289	setattr(module_this, name, udsid)
290
291# UDS = 0x7F [SID_requested] [NRC]
292UDS_NRC_DESCR = {
293	0x10	: "UDS_NRC_GENERAL_REJECT",
294	0x11	: "UDS_NRC_SERVICE_NOT_SUPPORTED",
295	0x12	: "UDS_NRC_SUBFUNCTION_NOT_SUPPORTED",
296	0x13	: "UDS_NRC_INCORRECT_MESSAGE_LENGTH_OR_FORMAT",
297	0x14	: "UDS_NRC_RESPONSE_TOO_BIG",
298	0x21	: "UDS_NRC_BUSY_REPEAT_REQUEST",
299	0x22	: "UDS_NRC_CONDITION_NOT_CORRECT",
300	0x24	: "UDS_NRC_REQUEST_SEQUENCE_ERROR",
301	0x25	: "UDS_NRC_NONRESPONSE_FROM_SUBNET_COMPONENT",
302	0x26	: "UDS_NRC_FAILURE_PREVENTS_EXEC_OR_ACTION",
303	0x31	: "UDS_NRC_REQUEST_OUT_OF_RANGE",
304	0x33	: "UDS_NRC_SECURITY_ACCESS_DENIED",
305	0x35	: "UDS_NRC_INVALID_KEY",
306	0x36	: "UDS_NRC_EXCEEDED_NUMBER_OF_ATTEMPTS",
307	0x37	: "UDS_NRC_REQUIRED_TIME_DELEAY_NOT_EXPIRED",
308	0x70	: "UDS_NRC_UPLOAD_DOWNLOAD_NOT_ACCEPTED",
309	0x71	: "UDS_NRC_TRANSFER_DATA_SUSPENDED",
310	0x72	: "UDS_NRC_GENERAL_PROGRAMMING_FAILURE",
311	0x73	: "UDS_NRC_WRONG_BLOCK_SEQUENCE_COUNTER",
312	0x78	: "UDS_NRC_REQUEST_CORRECTLY_RESPONSE_PENDING",
313	0x7E	: "UDS_NRC_SUBFUNCTION_NOT_SUPPORTED_IN_SESSION",
314	0x7F	: "UDS_NRC_SERVICE_NOT_SUPPORTED_IN_SESSION"
315}
316
317for x in range(0x38, 0x4F):
318	UDS_NRC_DESCR[x] = "UDS_NRC_RESERVED_%X" % x
319
320for udsid, name in UDS_NRC_DESCR.items():
321	setattr(module_this, name, udsid)
322
323
324class OBD2(pypacker.Packet):
325	__hdr__ = (
326		("mode", "B", 0),
327		("pid", "B", 0)
328	)
329
330
331class UDS(pypacker.Packet):
332	__hdr__ = (
333		("sid", "B", 0),
334		("lev", "B", 0)
335	)
336
337	def _dissect(self, buf):
338		# TODO: lev not always present
339		return 2
340
341
342ISOTP_TYPE_SF	= 0x00  # single frame
343ISOTP_TYPE_FF	= 0x01  # first frame
344ISOTP_TYPE_CF	= 0x02  # consecutive frame
345ISOTP_TYPE_FC	= 0x03  # flow control
346
347
348types_isotp_offset_upper = {
349	ISOTP_TYPE_SF: 1,
350	ISOTP_TYPE_FF: 2,
351	ISOTP_TYPE_CF: 1,
352	ISOTP_TYPE_FC: 3
353}
354
355types_isotp_offset_upper_got_type = {ISOTP_TYPE_SF, ISOTP_TYPE_FF}
356
357
358class ISOTPBase(pypacker.Packet):
359	def __get_sig(self):
360		return (self.pci & 0xF0) >> 4
361
362	def __set_sig(self, value):
363		self.pci = (value & 0xF) << 4 | (self.pci & 0xF)
364
365	sig = property(__get_sig, __set_sig)
366
367	def _dissect(self, buf):
368		#logger.debug("dissect in base class")
369		# OBD/UDS can one be differentiated in ISOTP_TYPE_SF and ISOTP_TYPE_FF
370		sig = buf[0] >> 4
371		#logger.debug("bytes in ISOTP: %r" % buf)
372		#logger.debug("ISOTP type: %X, offset: %d" % (sig, types_isotp_offset_upper[sig]))
373		#logger.debug("upper bytes: %r" % buf[types_isotp_offset_upper[sig]: ])
374
375		# check by request/response SID, on both OBD2 and UDS response will be SID+0x40
376		if sig in types_isotp_offset_upper_got_type or (sig - 0x40) in types_isotp_offset_upper_got_type:
377			obd_mode = buf[types_isotp_offset_upper[sig]]
378
379			if obd_mode in OBD2_MODE_DESCR:
380				# assume OBD2
381				#logger.debug("got OBD2")
382				self._init_handler(0, buf[types_isotp_offset_upper[sig]:])
383			else:
384				# assume UDS
385				#logger.debug("got UDS, will use bytes: %r" % buf[types_isotp_offset_upper[sig]: ])
386				self._init_handler(1, buf[types_isotp_offset_upper[sig]:])
387
388		return types_isotp_offset_upper[sig]
389
390
391class ISOTPSingleFrame(ISOTPBase):
392	__hdr__ = (
393		("pci", "B", ISOTP_TYPE_SF),
394	)
395
396	def __get_dl(self):
397		return self.pci & 0xF
398
399	def __set_dl(self, value):
400		self.pci = (self.pci & 0xF0) | (value & 0xF)
401
402	dl = property(__get_dl, __set_dl)
403
404	__handler__ = {
405		0: OBD2, 1: UDS
406	}
407
408
409class ISOTPFirstFrame(ISOTPBase):
410	__hdr__ = (
411		("pci", "H", ISOTP_TYPE_FF),
412	)
413
414	__handler__ = {
415		0: OBD2, 1: UDS
416	}
417
418	def __get_sig(self):
419		return (self.pci & 0xF000) >> 12
420
421	def __set_sig(self, value):
422		self.pci = (value & 0xF) << 12 | (self.pci & 0xFFF)
423
424	sig = property(__get_sig, __set_sig)
425
426	def __get_dl(self):
427		return self.pci & 0xFFF
428
429	def __set_dl(self, value):
430		self.pci = (self.pci & 0xF000) | (value & 0xFFF)
431
432	dl = property(__get_dl, __set_dl)
433
434
435class ISOTPConsecutiveFrame(ISOTPBase):
436	__hdr__ = (
437		("pci", "B", ISOTP_TYPE_CF),
438	)
439
440	__handler__ = {
441		0: OBD2, 1: UDS
442	}
443
444	def __get_sn(self):
445		return self.pci & 0xF
446
447	def __set_sn(self, value):
448		self.pci = (self.pci & 0xF0) | (value & 0xF)
449
450	sn = property(__get_sn, __set_sn)
451
452
453class ISOTPFlowControl(ISOTPBase):
454	__hdr__ = (
455		("pci", "B", ISOTP_TYPE_FC),
456		("pci_blocksize", "B", 0),
457		("pci_minsep", "B", 0),
458	)
459
460	__handler__ = {
461		0: OBD2, 1: UDS
462	}
463
464	def __get_flowstatus(self):
465		return self.pci & 0xF
466
467	def __set_flowstatus(self, value):
468		self.pci = (self.pci & 0xF0) | (value & 0xF)
469
470	flowstatus = property(__get_flowstatus, __set_flowstatus)
471
472
473isotp_type_class = {
474	ISOTP_TYPE_SF: ISOTPSingleFrame,
475	ISOTP_TYPE_FF: ISOTPFirstFrame,
476	ISOTP_TYPE_CF: ISOTPConsecutiveFrame,
477	ISOTP_TYPE_FC: ISOTPFlowControl
478}
479
480# CAN flags, little endian..wtf?
481CAN_MASK_EXT		= 0x80000000
482CAN_MASK_EXT_SHIFT	= 31
483CAN_MASK_RTR		= 0x40000000
484CAN_MASK_RTR_SHIFT	= 30
485CAN_MASK_ERR		= 0x20000000
486CAN_MASK_ERR_SHIFT	= 29
487# big endian
488MASK_ID			= 0x1FFFFFFF
489MASK_FLAGS		= ~MASK_ID
490
491
492ISOTP_PKT_CLZES = {ISOTPConsecutiveFrame, ISOTPFirstFrame, ISOTPFlowControl, ISOTPSingleFrame}
493
494
495class CAN(pypacker.Packet):
496	"""
497		Format:
498
499	The field containing the CAN ID and flags is in network byte order (big-endian).
500	The bottom 29 bits contain the CAN ID of the frame. The remaining bits are:
501
502	0x20000000 - set if the frame is an error message rather than a data frame.
503	0x40000000 - set if the frame is a remote transmission request frame.
504	0x80000000 - set if the frame is an extended 29-bit frame rather than a standard 11-bit frame. frame.
505	Source: http://www.tcpdump.org/linktypes/LINKTYPE_CAN_SOCKETCAN.html
506
507
508	SocketCan Packet, see https://www.kernel.org/doc/Documentation/networking/can.txt
509
510	struct can_frame {
511		canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
512		__u8    can_dlc; /* frame payload length in byte (0 .. 8) */
513		__u8    __pad;   /* padding */
514		__u8    __res0;  /* reserved / padding */
515		__u8    __res1;  /* reserved / padding */
516		__u8    data[8] __attribute__((aligned(8)));
517	};
518	special address description flags for the CAN_ID
519		CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
520		CAN_RTR_FLAG 0x40000000U /* remote transmission request */
521		CAN_ERR_FLAG 0x20000000U /* error message frame */
522
523	Native CAN structure (on wire):
524
525	CAN lengths (11-bit ID):
526	- Arbitration Field (12)
527	-- ID: unique id/prio (11)
528	-- Remote transmission request: 0/dominant for data, 1 for remote request (1)
529	- Control (6)
530	-- ID ext.: 0 for 11-bit ID (1)
531	-- Reserved: must be 0 (1)
532	-- Data length code: Number of bytes, 0 up to 8 (4)
533	- Data: 0 up to 8 bytes (8)
534
535	CAN lengths (29-bit ID):
536	- Arbitration Field (40)
537	-- ID: unique id/prio (11)
538	-- Substitute remote request, must be 1 (1)
539	-- Identifier extension bit: must be 1 (1)
540	-- ID: unique id/prio (18)
541	-- Remote transmission request: 0/dominant for data, 1 for remote request (1)
542	- Control (6)
543	-- Reserved: must be 0 (2)
544	-- Data length code: Number of bytes, 0 up to 8 (4)
545	- Data: 0 up to 8 bytes (8)
546
547	"""
548	__hdr__ = (
549		("flag_id", "I", 0),
550		("dlc", "B", 0),
551		("pad", "B", 0),
552		("res1", "B", 0),
553		("res2", "B", 0)
554	)
555
556	__handler__ = {
557		ISOTP_TYPE_SF: ISOTPSingleFrame,
558		ISOTP_TYPE_FF: ISOTPFirstFrame,
559		ISOTP_TYPE_CF: ISOTPConsecutiveFrame,
560		ISOTP_TYPE_FC: ISOTPFlowControl
561	}
562
563	def __get_extended(self):
564		return 0 if (self.flag_id & CAN_MASK_EXT) == 0 else 1
565
566	def __set_extended(self, value):
567		self.flag_id = (self.flag_id & ~CAN_MASK_EXT) | ((value & 1) << CAN_MASK_EXT_SHIFT)
568
569	extended = property(__get_extended, __set_extended)
570
571	def __get_rtr(self):
572		return 0 if (self.flag_id & CAN_MASK_RTR) == 0 else 1
573
574	def __set_rtr(self, value):
575		self.flag_id = (self.flag_id & ~CAN_MASK_RTR) | ((value & 1) << CAN_MASK_RTR_SHIFT)
576
577	rtr = property(__get_rtr, __set_rtr)
578
579	def __get_err(self):
580		return 0 if (self.flag_id & CAN_MASK_ERR) == 0 else 1
581
582	def __set_err(self, value):
583		self.flag_id = (self.flag_id & ~CAN_MASK_ERR) | ((value & 1) << CAN_MASK_ERR_SHIFT)
584
585	err = property(__get_err, __set_err)
586
587	def __get_id(self):
588		return self.flag_id & MASK_ID
589
590	def __set_id(self, value):
591		flags_be = self.flag_id & MASK_FLAGS
592		self.flag_id = flags_be | (value & MASK_ID)
593
594	id = property(__get_id, __set_id)
595
596	def _dissect(self, buf):
597		# assume ISO-TP
598		isotp_type = (buf[8] & 0xF0) >> 4
599		#logger.debug("got ISOTP type: %d, class will be: %r" %
600		#(isotp_type, isotp_type_class[isotp_type]))
601		self._init_handler(isotp_type, buf[8:])
602		return 8
603
604	def _update_fields(self):
605		if not self._header_changed:
606			return
607
608		if self.id > 0x7FF:
609			self.extended = 1
610		else:
611			self.extended = 0
612
613	def show_packet(self, prefix=""):
614		if self.body_handler.__class__ not in ISOTP_PKT_CLZES:
615			logger.warning("not an isotp packet: %r", self.body_handler)
616			return
617
618		isotp_pkt = self.body_handler
619		output = []
620		output.append("%s [ID: 0x%05X] " % (prefix, self.id))
621
622		uds_pkt = self[UDS]
623
624		if uds_pkt is not None:
625			if uds_pkt.sid in UDS_SID_DESCR:
626				output.append("-> %s" % UDS_SID_DESCR.get(uds_pkt.sid))
627			elif uds_pkt.sid - 0x40 in UDS_SID_DESCR:
628				output.append("<- %s" % UDS_SID_DESCR.get(uds_pkt.sid - 0x40))
629			elif uds_pkt.sid == module_this.UDS_NRC_SERVICE_NOT_SUPPORTED_IN_SESSION:
630				output.append("Err: %s" % UDS_NRC_DESCR.get(uds_pkt.bin()[2], "0x%X" % uds_pkt.bin()[2]))
631			else:
632				output.append("<unknown: %s>" % uds_pkt.sid)
633			output.append(", content: %r" % uds_pkt.bin())
634		else:
635			output.append(" %r, content: %s" % (isotp_pkt.__class__.__name__, isotp_pkt.body_bytes))
636
637		print("%s" % "".join(output))
638