Skip to content

Commit 74878fc

Browse files
automatically handle CAN and health packet ABI versions (commaai#2371)
1 parent c9ee50e commit 74878fc

File tree

10 files changed

+52
-47
lines changed

10 files changed

+52
-47
lines changed

SConscript

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import hashlib
23
import opendbc
34
import subprocess
45

@@ -150,6 +151,14 @@ with open("board/obj/cert.h", "w") as f:
150151
for cert in certs:
151152
f.write("\n".join(cert) + "\n")
152153

154+
# Packet version defines: SHA hash of the struct header files
155+
def version_hash(path):
156+
with open(path, "rb") as f:
157+
return int.from_bytes(hashlib.sha256(f.read()).digest()[:4], 'little')
158+
hh, ch, jh = version_hash("board/health.h"), version_hash(os.path.join(opendbc.INCLUDE_PATH, "opendbc/safety/can.h")), version_hash("board/jungle/jungle_health.h")
159+
common_flags += [f"-DHEALTH_PACKET_VERSION=0x{hh:08X}U", f"-DCAN_PACKET_VERSION_HASH=0x{ch:08X}U",
160+
f"-DJUNGLE_HEALTH_PACKET_VERSION=0x{jh:08X}U"]
161+
153162
# panda fw
154163
build_project("panda_h7", base_project_h7, "./board/main.c", [])
155164

board/body/main_comms.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
8989
break;
9090
}
9191

92-
// **** 0xdd: get healthpacket and CANPacket versions
93-
case 0xdd:
94-
resp[0] = HEALTH_PACKET_VERSION;
95-
resp[1] = CAN_PACKET_VERSION;
96-
resp[2] = CAN_HEALTH_PACKET_VERSION;
97-
resp_len = 3U;
92+
// **** 0xdd: get healthpacket and CANPacket version hashes
93+
case 0xdd: {
94+
uint32_t versions[2] = {HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH};
95+
(void)memcpy(resp, (uint8_t *)versions, sizeof(versions));
96+
resp_len = sizeof(versions);
9897
break;
98+
}
9999

100100
default:
101101
// Ignore unhandled requests

board/health.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
#pragma once
22

3-
// When changing these structs, python/__init__.py needs to be kept up to date!
4-
5-
#define HEALTH_PACKET_VERSION 18
63
struct __attribute__((packed)) health_t {
74
uint32_t uptime_pkt;
85
uint32_t voltage_pkt;
@@ -32,7 +29,6 @@ struct __attribute__((packed)) health_t {
3229
uint16_t sound_output_level_pkt;
3330
};
3431

35-
#define CAN_HEALTH_PACKET_VERSION 5
3632
typedef struct __attribute__((packed)) {
3733
uint8_t bus_off;
3834
uint32_t bus_off_cnt;

board/jungle/__init__.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from functools import wraps
55

66
from panda import Panda, PandaDFU
7-
from panda.python.constants import McuType
7+
from panda.python.constants import McuType, compute_version_hash
88

99
BASEDIR = os.path.dirname(os.path.realpath(__file__))
1010
FW_PATH = os.path.join(BASEDIR, "../obj/")
@@ -39,7 +39,7 @@ class PandaJungle(Panda):
3939
H7_DEVICES = [HW_TYPE_V2, ]
4040
SUPPORTED_DEVICES = H7_DEVICES
4141

42-
HEALTH_PACKET_VERSION = 1
42+
HEALTH_PACKET_VERSION = compute_version_hash(os.path.join(BASEDIR, "jungle_health.h"))
4343
HEALTH_STRUCT = struct.Struct("<IffffffHHHHHHHHHHHH")
4444

4545
HARNESS_ORIENTATION_NONE = 0
@@ -108,13 +108,11 @@ def health(self):
108108

109109
# ******************* control *******************
110110

111-
# Returns tuple with health packet version and CAN packet/USB packet version
112111
def get_packets_versions(self):
113-
dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xdd, 0, 0, 3)
114-
if dat and len(dat) == 3:
115-
a = struct.unpack("BBB", dat)
116-
return (a[0], a[1], a[2])
117-
return (-1, -1, -1)
112+
dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xdd, 0, 0, 8)
113+
if dat and len(dat) == 8:
114+
return struct.unpack("<II", dat)
115+
return (0, 0)
118116

119117
# ******************* jungle stuff *******************
120118

board/jungle/jungle_health.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// When changing these structs, python/__init__.py needs to be kept up to date!
2-
3-
#define JUNGLE_HEALTH_PACKET_VERSION 1
41
struct __attribute__((packed)) jungle_health_t {
52
uint32_t uptime_pkt;
63
float ch1_power;

board/jungle/main_comms.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,13 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
180180
current_board->set_can_mode(CAN_MODE_NORMAL);
181181
}
182182
break;
183-
// **** 0xdd: get healthpacket and CANPacket versions
184-
case 0xdd:
185-
resp[0] = JUNGLE_HEALTH_PACKET_VERSION;
186-
resp[1] = CAN_PACKET_VERSION;
187-
resp[2] = CAN_HEALTH_PACKET_VERSION;
188-
resp_len = 3;
183+
// **** 0xdd: get healthpacket and CANPacket version hashes
184+
case 0xdd: {
185+
uint32_t versions[2] = {JUNGLE_HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH};
186+
(void)memcpy(resp, (uint8_t *)versions, sizeof(versions));
187+
resp_len = sizeof(versions);
189188
break;
189+
}
190190
// **** 0xde: set can bitrate
191191
case 0xde:
192192
if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) {

board/main_comms.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,13 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
226226
case 0xdc:
227227
set_safety_mode(req->param1, (uint16_t)req->param2);
228228
break;
229-
// **** 0xdd: get healthpacket and CANPacket versions
230-
case 0xdd:
231-
resp[0] = HEALTH_PACKET_VERSION;
232-
resp[1] = CAN_PACKET_VERSION;
233-
resp[2] = CAN_HEALTH_PACKET_VERSION;
234-
resp_len = 3;
229+
// **** 0xdd: get health and CAN packet versions
230+
case 0xdd: {
231+
uint32_t versions[2] = {HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH};
232+
(void)memcpy(resp, (uint8_t *)versions, sizeof(versions));
233+
resp_len = sizeof(versions);
235234
break;
235+
}
236236
// **** 0xde: set can bitrate
237237
case 0xde:
238238
if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) {

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ packages = [
4949
"panda.board.jungle",
5050
]
5151

52+
[tool.setuptools.package-data]
53+
"panda.board" = ["health.h"]
54+
"panda.board.jungle" = ["jungle_health.h"]
55+
5256
[tool.setuptools.package-dir]
5357
panda = "."
5458

python/__init__.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
from functools import wraps, partial
1111
from itertools import accumulate
1212

13+
import opendbc
1314
from opendbc.car.structs import CarParams
1415

1516
from .base import BaseHandle
16-
from .constants import FW_PATH, McuType
17+
from .constants import BASEDIR, FW_PATH, McuType, compute_version_hash
1718
from .dfu import PandaDFU
1819
from .spi import PandaSpiHandle, PandaSpiException, PandaProtocolMismatch
1920
from .usb import PandaUsbHandle
@@ -104,7 +105,6 @@ def wrapper(self, *args, **kwargs):
104105
return fn(self, *args, **kwargs)
105106
return wrapper
106107
ensure_can_packet_version = partial(ensure_version, "CAN", "CAN_PACKET_VERSION", "can_version")
107-
ensure_can_health_packet_version = partial(ensure_version, "CAN health", "CAN_HEALTH_PACKET_VERSION", "can_health_version")
108108
ensure_health_packet_version = partial(ensure_version, "health", "HEALTH_PACKET_VERSION", "health_version")
109109

110110

@@ -126,9 +126,8 @@ class Panda:
126126
HW_TYPE_CUATRO = b'\x0a'
127127
HW_TYPE_BODY = b'\xb1'
128128

129-
CAN_PACKET_VERSION = 4
130-
HEALTH_PACKET_VERSION = 18
131-
CAN_HEALTH_PACKET_VERSION = 5
129+
CAN_PACKET_VERSION = compute_version_hash(os.path.join(opendbc.INCLUDE_PATH, "opendbc/safety/can.h"))
130+
HEALTH_PACKET_VERSION = compute_version_hash(os.path.join(BASEDIR, "board/health.h"))
132131
HEALTH_STRUCT = struct.Struct("<IIIIIIIIBBBBBHBBBHfBBHHHBH")
133132
CAN_HEALTH_STRUCT = struct.Struct("<BIBBBBBBBBIIIIIIIHHBBBIIII")
134133

@@ -210,7 +209,7 @@ def connect(self, claim=True, wait=False):
210209
self._serial = serial
211210
self._connect_serial = serial
212211
self._handle_open = True
213-
self.health_version, self.can_version, self.can_health_version = self.get_packets_versions()
212+
self.health_version, self.can_version = self.get_packets_versions()
214213
logger.debug("connected")
215214

216215
# disable openpilot's heartbeat checks
@@ -536,7 +535,7 @@ def health(self):
536535
"sound_output_level": a[25],
537536
}
538537

539-
@ensure_can_health_packet_version
538+
@ensure_health_packet_version
540539
def can_health(self, can_number):
541540
LEC_ERROR_CODE = {
542541
0: "No error",
@@ -598,14 +597,11 @@ def get_signature(self) -> bytes:
598597
def get_type(self):
599598
return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40)
600599

601-
# Returns tuple with health packet version and CAN packet/USB packet version
602600
def get_packets_versions(self):
603-
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xdd, 0, 0, 3)
604-
if dat and len(dat) == 3:
605-
a = struct.unpack("BBB", dat)
606-
return (a[0], a[1], a[2])
607-
else:
608-
return (0, 0, 0)
601+
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xdd, 0, 0, 8)
602+
if dat and len(dat) == 8:
603+
return struct.unpack("<II", dat)
604+
return (0, 0)
609605

610606
def is_internal(self):
611607
return self.get_type() in Panda.INTERNAL_DEVICES

python/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import os
22
import enum
3+
import hashlib
34
from typing import NamedTuple
45

56
BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
67
FW_PATH = os.path.join(BASEDIR, "board/obj/")
78

9+
def compute_version_hash(filepath):
10+
with open(filepath, "rb") as f:
11+
return int.from_bytes(hashlib.sha256(f.read()).digest()[:4], 'little')
12+
813
USBPACKET_MAX_SIZE = 0x40
914

1015
class McuConfig(NamedTuple):

0 commit comments

Comments
 (0)