diff --git a/python/__init__.py b/python/__init__.py index 71d416bd..424dd55c 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -227,8 +227,6 @@ class Panda: self._disable_checks = disable_checks self._handle = None - self._bcd_device = None - self.can_rx_overflow_buffer = b'' # connect and set mcu type @@ -253,13 +251,30 @@ class Panda: self._handle = None # try USB first, then SPI - self._handle, serial, self.bootstub, self._bcd_device = self.usb_connect(self._serial, claim=claim, wait=wait) + self._handle, serial, self.bootstub, bcd = self.usb_connect(self._serial, claim=claim, wait=wait) if self._handle is None: - self._handle, serial, self.bootstub, _ = self.spi_connect(self._serial) + self._handle, serial, self.bootstub, bcd = self.spi_connect(self._serial) if self._handle is None: raise Exception("failed to connect to panda") + # Some fallback logic to determine panda and MCU type for old bootstubs, + # since we now support multiple MCUs and need to know which fw to flash. + # Three cases to consider: + # A) oldest bootstubs don't have any way to distinguish + # MCU or panda type + # B) slightly newer (~2 weeks after first C3's built) bootstubs + # have the panda type set in the USB bcdDevice + # C) latest bootstubs also implement the endpoint for panda type + self._bcd_hw_type = None + ret = self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40) + missing_hw_type_endpoint = self.bootstub and ret.startswith(b'\xff\x00\xc1\x3e\xde\xad\xd0\x0d') + if missing_hw_type_endpoint and bcd is not None: + self._bcd_hw_type = bcd + + # For case A, we assume F4 MCU type, since all H7 pandas should be case B at worst + self._assume_f4_mcu = (self._bcd_hw_type is None) and missing_hw_type_endpoint + self._serial = serial self._mcu_type = self.get_mcu_type() self.health_version, self.can_version, self.can_health_version = self.get_packets_versions() @@ -584,10 +599,9 @@ class Panda: def get_type(self): ret = self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40) - # bootstub doesn't implement this call, so fallback to bcdDevice - invalid_type = self.bootstub and (ret is None or len(ret) != 1) - if invalid_type and self._bcd_device is not None: - ret = self._bcd_device + # old bootstubs don't implement this endpoint, see comment in Panda.device + if self._bcd_hw_type is not None and (ret is None or len(ret) != 1): + ret = self._bcd_hw_type return ret @@ -608,6 +622,11 @@ class Panda: return McuType.F4 elif hw_type in Panda.H7_DEVICES: return McuType.H7 + else: + # have to assume F4, see comment in Panda.connect + if self._assume_f4_mcu: + return McuType.F4 + raise ValueError(f"unknown HW type: {hw_type}") def has_obd(self): diff --git a/tests/hitl/1_program.py b/tests/hitl/1_program.py index 20c1be21..c129c99b 100644 --- a/tests/hitl/1_program.py +++ b/tests/hitl/1_program.py @@ -5,6 +5,8 @@ from panda import Panda, PandaDFU, McuType, BASEDIR from .helpers import test_all_pandas, panda_connect_and_init, check_signature +# TODO: make more comprehensive bootstub tests and run on a few production ones + current +# TODO: also test release-signed app @test_all_pandas @panda_connect_and_init def test_a_known_bootstub(p): @@ -12,25 +14,44 @@ def test_a_known_bootstub(p): Test that compiled app can work with known production bootstub """ known_bootstubs = { - McuType.F4: "bootstub.panda.bin", - McuType.H7: "bootstub.panda_h7.bin", + # covers the two cases listed in Panda.connect + McuType.F4: [ + # case A - no bcdDevice or panda type, has to assume F4 + "bootstub_f4_first_dos_production.panda.bin", + + # case B - just bcdDevice + "bootstub_f4_only_bcd.panda.bin", + ], + McuType.H7: ["bootstub.panda_h7.bin"], } - p.reset(enter_bootstub=True) - p.reset(enter_bootloader=True) + for kb in known_bootstubs[p.get_mcu_type()]: + app_ids = (p.get_mcu_type(), p.get_usb_serial()) + assert None not in app_ids - dfu_serial = PandaDFU.st_serial_to_dfu_serial(p._serial, p._mcu_type) - assert Panda.wait_for_dfu(dfu_serial, timeout=30) + p.reset(enter_bootstub=True) + p.reset(enter_bootloader=True) - dfu = PandaDFU(dfu_serial) - fn = known_bootstubs[p._mcu_type] - with open(os.path.join(BASEDIR, "tests/hitl/known_bootstub", fn), "rb") as f: - code = f.read() + dfu_serial = PandaDFU.st_serial_to_dfu_serial(p._serial, p._mcu_type) + assert Panda.wait_for_dfu(dfu_serial, timeout=30) - dfu.program_bootstub(code) - p.connect(True, True) - p.flash() - check_signature(p) + dfu = PandaDFU(dfu_serial) + with open(os.path.join(BASEDIR, "tests/hitl/known_bootstub", kb), "rb") as f: + code = f.read() + + dfu.program_bootstub(code) + + p.connect(claim=False, wait=True) + + # check for MCU or serial mismatch + with Panda(p._serial, claim=False) as np: + bootstub_ids = (np.get_mcu_type(), np.get_usb_serial()) + assert app_ids == bootstub_ids + + # ensure we can flash app and it jumps to app + p.flash() + check_signature(p) + assert not p.bootstub @test_all_pandas @panda_connect_and_init diff --git a/tests/hitl/known_bootstub/bootstub.panda.bin b/tests/hitl/known_bootstub/bootstub.panda.bin deleted file mode 100755 index 6f658e2d..00000000 Binary files a/tests/hitl/known_bootstub/bootstub.panda.bin and /dev/null differ diff --git a/tests/hitl/known_bootstub/bootstub.panda_h7.bin b/tests/hitl/known_bootstub/bootstub.panda_h7.bin old mode 100755 new mode 100644 diff --git a/tests/hitl/known_bootstub/bootstub_f4_first_dos_production.panda.bin b/tests/hitl/known_bootstub/bootstub_f4_first_dos_production.panda.bin new file mode 100644 index 00000000..786acf63 Binary files /dev/null and b/tests/hitl/known_bootstub/bootstub_f4_first_dos_production.panda.bin differ diff --git a/tests/hitl/known_bootstub/bootstub_f4_only_bcd.panda.bin b/tests/hitl/known_bootstub/bootstub_f4_only_bcd.panda.bin new file mode 100755 index 00000000..000fd26f Binary files /dev/null and b/tests/hitl/known_bootstub/bootstub_f4_only_bcd.panda.bin differ