diff --git a/README.md b/README.md index 07825cf4..327938ca 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Welcome to panda -![panda tests](https://github.com/commaai/panda/workflows/tests/badge.svg) - panda speaks CAN and CAN FD, and it runs on [STM32F413](https://www.st.com/resource/en/reference_manual/rm0430-stm32f413423-advanced-armbased-32bit-mcus-stmicroelectronics.pdf) and [STM32H725](https://www.st.com/resource/en/reference_manual/rm0468-stm32h723733-stm32h725735-and-stm32h730-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf). ## Directory structure @@ -11,7 +9,9 @@ panda speaks CAN and CAN FD, and it runs on [STM32F413](https://www.st.com/resou ├── board # Code that runs on the STM32 ├── drivers # Drivers (not needed for use with Python) ├── python # Python userspace library for interfacing with the panda -├── tests # Tests and helper programs for panda +├── tests # Tests for panda +├── scripts # Miscellaneous used for panda development and debugging +├── examples # Example scripts for using a panda in a car ``` ## Safety Model diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 0b6de30d..00000000 --- a/mypy.ini +++ /dev/null @@ -1,11 +0,0 @@ -[mypy] -; third-party packages -ignore_missing_imports = True - -; helpful warnings -warn_redundant_casts = True -warn_unreachable = True -warn_unused_ignores = True - -; restrict dynamic typing -warn_return_any = True diff --git a/panda.png b/panda.png deleted file mode 100644 index e18137d8..00000000 Binary files a/panda.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 5b0697d6..0f616b3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,3 +13,16 @@ flake8-implicit-str-concat.allow-multiline=false [tool.pytest.ini_options] addopts = "-n auto --ignore-glob='*.sh'" + + +[tool.mypy] +# third-party packages +ignore_missing_imports = true + +# helpful warnings +warn_redundant_casts = true +warn_unreachable = true +warn_unused_ignores = true + +# restrict dynamic typing +warn_return_any = true diff --git a/release/.gitignore b/release/.gitignore deleted file mode 100644 index c4c4ffc6..00000000 --- a/release/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.zip diff --git a/tests/benchmark.py b/scripts/benchmark.py similarity index 100% rename from tests/benchmark.py rename to scripts/benchmark.py diff --git a/tests/black_white_loopback_test.py b/scripts/black_white_loopback_test.py similarity index 100% rename from tests/black_white_loopback_test.py rename to scripts/black_white_loopback_test.py diff --git a/tests/black_white_relay_endurance.py b/scripts/black_white_relay_endurance.py similarity index 100% rename from tests/black_white_relay_endurance.py rename to scripts/black_white_relay_endurance.py diff --git a/tests/black_white_relay_test.py b/scripts/black_white_relay_test.py similarity index 100% rename from tests/black_white_relay_test.py rename to scripts/black_white_relay_test.py diff --git a/tests/bulk_write_test.py b/scripts/bulk_write_test.py similarity index 100% rename from tests/bulk_write_test.py rename to scripts/bulk_write_test.py diff --git a/tests/can_health.py b/scripts/can_health.py similarity index 100% rename from tests/can_health.py rename to scripts/can_health.py diff --git a/tests/can_printer.py b/scripts/can_printer.py similarity index 100% rename from tests/can_printer.py rename to scripts/can_printer.py diff --git a/tests/check_fw_size.py b/scripts/check_fw_size.py similarity index 100% rename from tests/check_fw_size.py rename to scripts/check_fw_size.py diff --git a/tests/ci_shell.sh b/scripts/ci_shell.sh similarity index 100% rename from tests/ci_shell.sh rename to scripts/ci_shell.sh diff --git a/tests/debug_console.py b/scripts/debug_console.py similarity index 100% rename from tests/debug_console.py rename to scripts/debug_console.py diff --git a/tests/development/register_hashmap_spread.py b/scripts/development/register_hashmap_spread.py similarity index 100% rename from tests/development/register_hashmap_spread.py rename to scripts/development/register_hashmap_spread.py diff --git a/tests/echo.py b/scripts/echo.py similarity index 100% rename from tests/echo.py rename to scripts/echo.py diff --git a/tests/fan/fan_test.py b/scripts/fan/fan_test.py similarity index 100% rename from tests/fan/fan_test.py rename to scripts/fan/fan_test.py diff --git a/tests/fan/fan_tuning.py b/scripts/fan/fan_tuning.py similarity index 100% rename from tests/fan/fan_tuning.py rename to scripts/fan/fan_tuning.py diff --git a/tests/get_version.py b/scripts/get_version.py similarity index 100% rename from tests/get_version.py rename to scripts/get_version.py diff --git a/tests/health_test.py b/scripts/health_test.py similarity index 100% rename from tests/health_test.py rename to scripts/health_test.py diff --git a/tests/ir_test.py b/scripts/ir_test.py similarity index 100% rename from tests/ir_test.py rename to scripts/ir_test.py diff --git a/tests/loopback_test.py b/scripts/loopback_test.py similarity index 100% rename from tests/loopback_test.py rename to scripts/loopback_test.py diff --git a/release/make_release.sh b/scripts/make_release.sh similarity index 100% rename from release/make_release.sh rename to scripts/make_release.sh diff --git a/tests/message_drop_test.py b/scripts/message_drop_test.py similarity index 100% rename from tests/message_drop_test.py rename to scripts/message_drop_test.py diff --git a/tests/read_flash_spi.py b/scripts/read_flash_spi.py similarity index 100% rename from tests/read_flash_spi.py rename to scripts/read_flash_spi.py diff --git a/tests/read_st_flash.sh b/scripts/read_st_flash.sh similarity index 100% rename from tests/read_st_flash.sh rename to scripts/read_st_flash.sh diff --git a/tests/reflash_internal_panda.py b/scripts/reflash_internal_panda.py similarity index 100% rename from tests/reflash_internal_panda.py rename to scripts/reflash_internal_panda.py diff --git a/tests/relay_test.py b/scripts/relay_test.py similarity index 100% rename from tests/relay_test.py rename to scripts/relay_test.py diff --git a/tests/restore_flash_spi.py b/scripts/restore_flash_spi.py similarity index 100% rename from tests/restore_flash_spi.py rename to scripts/restore_flash_spi.py diff --git a/tests/setup_device_ci.sh b/scripts/setup_device_ci.sh similarity index 100% rename from tests/setup_device_ci.sh rename to scripts/setup_device_ci.sh diff --git a/tests/som_debug.sh b/scripts/som_debug.sh similarity index 100% rename from tests/som_debug.sh rename to scripts/som_debug.sh diff --git a/tests/spam_can.py b/scripts/spam_can.py similarity index 100% rename from tests/spam_can.py rename to scripts/spam_can.py diff --git a/tests/standalone_test.py b/scripts/standalone_test.py similarity index 100% rename from tests/standalone_test.py rename to scripts/standalone_test.py diff --git a/tests/canfd/test_canfd.py b/scripts/test_canfd.py similarity index 100% rename from tests/canfd/test_canfd.py rename to scripts/test_canfd.py diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/elm_car_simulator.py b/tests/elm_car_simulator.py deleted file mode 100755 index e297eb04..00000000 --- a/tests/elm_car_simulator.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env python3 - -"""Used to Reverse/Test ELM protocol auto detect and OBD message response without a car.""" - -import os -import sys -import struct -import binascii -import time -import threading -from collections import deque - -from opendbc.car.structs import CarParams -from panda import Panda - -def lin_checksum(dat): - return sum(dat) % 0x100 - -class ELMCarSimulator(): - def __init__(self, sn, silent=False, can_kbaud=500, - can=True, can11b=True, can29b=True, - lin=True): - self.__p = Panda(sn if sn else Panda.list()[0]) - self.__on = True - self.__stop = False - self.__silent = silent - - self.__lin_timer = None - self.__lin_active = False - self.__lin_enable = lin - self.__lin_monitor_thread = threading.Thread(target=self.__lin_monitor) - - self.__can_multipart_data = None - self.__can_kbaud = can_kbaud - self.__can_extra_noise_msgs = deque() - self.__can_enable = can - self.__can11b = can11b - self.__can29b = can29b - self.__can_monitor_thread = threading.Thread(target=self.__can_monitor) - - @property - def panda(self): - return self.__p - - def stop(self): - if self.__lin_timer: - self.__lin_timer.cancel() - self.__lin_timeout_handler() - - self.__stop = True - - def join(self): - if self.__lin_monitor_thread.is_alive(): - self.__lin_monitor_thread.join() - if self.__can_monitor_thread.is_alive(): - self.__can_monitor_thread.join() - if self.__p: - print("closing handle") - self.__p.close() - - def set_enable(self, on): - self.__on = on - - def start(self): - self.panda.set_safety_mode(CarParams.SafetyModel.allOutput) - if self.__lin_enable: - self.__lin_monitor_thread.start() - if self.__can_enable: - self.__can_monitor_thread.start() - - ######################### - # CAN related functions # - ######################### - - def __can_monitor(self): - print("STARTING CAN THREAD") - self.panda.set_can_speed_kbps(0, self.__can_kbaud) - self.panda.can_recv() # Toss whatever was already there - - while not self.__stop: - for address, data, src in self.panda.can_recv(): - if self.__on and src == 0 and len(data) == 8 and data[0] >= 2: - if not self.__silent: - print("Processing CAN message", src, hex(address), binascii.hexlify(data)) - self.__can_process_msg(data[1], data[2], address, data, src) - elif not self.__silent: - print("Rejecting CAN message", src, hex(address), binascii.hexlify(data)) - - def can_mode_11b(self): - self.__can11b = True - self.__can29b = False - - def can_mode_29b(self): - self.__can11b = False - self.__can29b = True - - def can_mode_11b_29b(self): - self.__can11b = True - self.__can29b = True - - def change_can_baud(self, kbaud): - self.__can_kbaud = kbaud - self.panda.set_can_speed_kbps(0, self.__can_kbaud) - - def can_add_extra_noise(self, noise_msg, addr=None): - self.__can_extra_noise_msgs.append((addr, noise_msg)) - - def _can_send(self, addr, msg): - if not self.__silent: - print(" CAN Reply (%x)" % addr, binascii.hexlify(msg)) - self.panda.can_send(addr, msg + b'\x00' * (8 - len(msg)), 0) - if self.__can_extra_noise_msgs: - noise = self.__can_extra_noise_msgs.popleft() - self.panda.can_send(noise[0] if noise[0] is not None else addr, - noise[1] + b'\x00' * (8 - len(noise[1])), 0) - - def _can_addr_matches(self, addr): - if self.__can11b and (addr == 0x7DF or (addr & 0x7F8) == 0x7E0): - return True - if self.__can29b and (addr == 0x18db33f1 or (addr & 0x1FFF00FF) == 0x18da00f1): - return True - return False - - def __can_process_msg(self, mode, pid, address, data, src): - if not self.__silent: - print("CAN MSG", binascii.hexlify(data[1:1 + data[0]]), - "Addr:", hex(address), "Mode:", hex(mode)[2:].zfill(2), - "PID:", hex(pid)[2:].zfill(2), "canLen:", len(data), - binascii.hexlify(data)) - - if self._can_addr_matches(address) and len(data) == 8: - outmsg = None - if data[:3] == b'\x30\x00\x00' and len(self.__can_multipart_data): - if not self.__silent: - print("Request for more data") - outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110 - msgnum = 1 - while(self.__can_multipart_data): - datalen = min(7, len(self.__can_multipart_data)) - msgpiece = struct.pack("B", 0x20 | msgnum) + self.__can_multipart_data[:datalen] - self._can_send(outaddr, msgpiece) - self.__can_multipart_data = self.__can_multipart_data[7:] - msgnum = (msgnum + 1) % 0x10 - time.sleep(0.01) - - else: - outmsg = self._process_obd(mode, pid) - - if outmsg: - outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110 - - if len(outmsg) <= 5: - self._can_send(outaddr, - struct.pack("BBB", len(outmsg) + 2, 0x40 | data[1], pid) + outmsg) - else: - first_msg_len = min(3, len(outmsg) % 7) - payload_len = len(outmsg) + 3 - msgpiece = struct.pack("BBBBB", 0x10 | ((payload_len >> 8) & 0xF), - payload_len & 0xFF, - 0x40 | data[1], pid, 1) + outmsg[:first_msg_len] - self._can_send(outaddr, msgpiece) - self.__can_multipart_data = outmsg[first_msg_len:] - - ######################### - # General OBD functions # - ######################### - - def _process_obd(self, mode, pid): - if mode == 0x01: # Mode: Show current data - if pid == 0x00: # List supported things - return b"\xff\xff\xff\xfe" # b"\xBE\x1F\xB8\x10" #Bitfield, random features - elif pid == 0x01: # Monitor Status since DTC cleared - return b"\x00\x00\x00\x00" # Bitfield, random features - elif pid == 0x04: # Calculated engine load - return b"\x2f" - elif pid == 0x05: # Engine coolant temperature - return b"\x3c" - elif pid == 0x0B: # Intake manifold absolute pressure - return b"\x90" - elif pid == 0x0C: # Engine RPM - return b"\x1A\xF8" - elif pid == 0x0D: # Vehicle Speed - return b"\x53" - elif pid == 0x10: # MAF air flow rate - return b"\x01\xA0" - elif pid == 0x11: # Throttle Position - return b"\x90" - elif pid == 0x33: # Absolute Barometric Pressure - return b"\x90" - elif mode == 0x09: # Mode: Request vehicle information - if pid == 0x02: # Show VIN - return b"1D4GP00R55B123456" - if pid == 0xFC: # test long multi message. Ligned up for LIN responses - return b''.join(struct.pack(">BBH", 0xAA, 0xAA, num + 1) for num in range(80)) - if pid == 0xFD: # test long multi message - parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(80)) - return b'\xAA\xAA\xAA' + b''.join(parts) - if pid == 0xFE: # test very long multi message - parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(584)) - return b'\xAA\xAA\xAA' + b''.join(parts) + b'\xAA' - if pid == 0xFF: - return b'\xAA\x00\x00' + \ - b"".join((b'\xAA' * 5) + struct.pack(">H", num + 1) for num in range(584)) - #return b"\xAA"*100#(0xFFF-3) - - -if __name__ == "__main__": - serial = os.getenv("SERIAL") if os.getenv("SERIAL") else None - kbaud = int(os.getenv("CANKBAUD")) if os.getenv("CANKBAUD") else 500 # type: ignore - bitwidth = int(os.getenv("CANBITWIDTH")) if os.getenv("CANBITWIDTH") else 0 # type: ignore - canenable = bool(int(os.getenv("CANENABLE"))) if os.getenv("CANENABLE") else True # type: ignore - linenable = bool(int(os.getenv("LINENABLE"))) if os.getenv("LINENABLE") else True # type: ignore - sim = ELMCarSimulator(serial, can_kbaud=kbaud, can=canenable, lin=linenable) - if(bitwidth == 0): - sim.can_mode_11b_29b() - if(bitwidth == 11): - sim.can_mode_11b() - if(bitwidth == 29): - sim.can_mode_29b() - - import signal - - def signal_handler(signal, frame): - print('\nShutting down simulator') - sim.stop() - sim.join() - sys.exit(0) - - signal.signal(signal.SIGINT, signal_handler) - - sim.start() - - signal.pause() diff --git a/tests/elm_throughput.py b/tests/elm_throughput.py deleted file mode 100755 index 983d4a14..00000000 --- a/tests/elm_throughput.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python3 - -import socket -import threading -import select - -class Reader(threading.Thread): - def __init__(self, s, *args, **kwargs): - super().__init__(*args, **kwargs) - self._s = s - self.__stop = False - - def stop(self): - self.__stop = True - - def run(self): - while not self.__stop: - s.recv(1000) - -def read_or_fail(s): - ready = select.select([s], [], [], 4) - assert ready[0], "Socket did not receive data within the timeout duration." - return s.recv(1000) - -def send_msg(s, msg): - s.send(msg) - res = b'' - while not res.endswith(">"): - res += read_or_fail(s) - return res - -if __name__ == "__main__": - s = socket.create_connection(("192.168.0.10", 35000)) - send_msg(s, b"ATZ\r") - send_msg(s, b"ATL1\r") - print(send_msg(s, b"ATE0\r")) - print(send_msg(s, b"ATS0\r")) - print(send_msg(s, b"ATSP6\r")) - - print("\nLOOP\n") - - while True: - print(send_msg(s, b"0100\r")) - print(send_msg(s, b"010d\r")) diff --git a/tests/read_winusb_descriptors.py b/tests/read_winusb_descriptors.py deleted file mode 100755 index 5d311c9e..00000000 --- a/tests/read_winusb_descriptors.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore -from panda import Panda -from hexdump import hexdump - -DEBUG = False - -if __name__ == "__main__": - p = Panda() - - length = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, 1) - print('Microsoft OS String Descriptor') - dat = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, length[0]) - if DEBUG: - print(f'LEN: {hex(length[0])}') - hexdump("".join(map(chr, dat))) - - ms_vendor_code = dat[16] - if DEBUG: - print(f'MS_VENDOR_CODE: {hex(length[0])}') - - print('\nMicrosoft Compatible ID Feature Descriptor') - length = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, 1) - if DEBUG: - print(f'LEN: {hex(length[0])}') - dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, length[0]) - hexdump("".join(map(chr, dat))) - - print('\nMicrosoft Extended Properties Feature Descriptor') - length = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, 1) - if DEBUG: - print(f'LEN: {hex(length[0])}') - dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, length[0]) - hexdump("".join(map(chr, dat))) diff --git a/tests/test_rsa.c b/tests/test_rsa.c deleted file mode 100644 index 5c784e23..00000000 --- a/tests/test_rsa.c +++ /dev/null @@ -1,34 +0,0 @@ -/* -gcc -DTEST_RSA test_rsa.c ../crypto/rsa.c ../crypto/sha.c && ./a.out -*/ - -#include -#include - -#define MAX_LEN 0x40000 -char buf[MAX_LEN]; - -#include "../crypto/sha.h" -#include "../crypto/rsa.h" -#include "../obj/cert.h" - -int main() { - FILE *f = fopen("../obj/panda.bin", "rb"); - int tlen = fread(buf, 1, MAX_LEN, f); - fclose(f); - printf("read %d\n", tlen); - uint32_t *_app_start = (uint32_t *)buf; - - int len = _app_start[0]; - char digest[SHA_DIGEST_SIZE]; - SHA_hash(&_app_start[1], len-4, digest); - printf("SHA hash done\n"); - - if (!RSA_verify(&rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) { - printf("RSA fail\n"); - } else { - printf("RSA match!!!\n"); - } - - return 0; -}