dragonpilot beta3

date: 2023-10-09T10:55:55
commit: 91b6e3aecd7170f24bccacb10c515ec281c30295
This commit is contained in:
dragonpilot
2023-10-09 10:54:45 -07:00
parent a9ba3193e2
commit cfe0ae9b8a
518 changed files with 159749 additions and 17401 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
from .python.constants import McuType, BASEDIR, FW_PATH, USBPACKET_MAX_SIZE # noqa: F401
from .python.spi import PandaSpiException, PandaProtocolMismatch # noqa: F401
from .python.spi import PandaSpiException, PandaProtocolMismatch, STBootloaderSPIHandle # noqa: F401
from .python.serial import PandaSerial # noqa: F401
from .python.canhandle import CanHandle # noqa: F401
from .python import (Panda, PandaDFU, # noqa: F401
pack_can_buffer, unpack_can_buffer, calculate_checksum, unpack_log,
pack_can_buffer, unpack_can_buffer, calculate_checksum,
DLC_TO_LEN, LEN_TO_DLC, ALTERNATIVE_EXPERIENCE, CANPACKET_HEAD_SIZE)
+4 -1
View File
@@ -50,7 +50,7 @@ class PandaJungle(Panda):
@classmethod
def spi_connect(cls, serial, ignore_version=False):
return None, None, None, None
return None, None, None, None, None
def flash(self, fn=None, code=None, reconnect=True):
if not fn:
@@ -132,6 +132,9 @@ class PandaJungle(Panda):
def set_panda_power(self, enabled):
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa0, int(enabled), 0, b'')
def set_panda_individual_power(self, port, enabled):
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa3, int(port), int(enabled), b'')
def set_harness_orientation(self, mode):
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa1, int(mode), 0, b'')
@@ -4,6 +4,7 @@ typedef void (*board_set_led)(uint8_t color, bool enabled);
typedef void (*board_board_tick)(void);
typedef bool (*board_get_button)(void);
typedef void (*board_set_panda_power)(bool enabled);
typedef void (*board_set_panda_individual_power)(uint8_t port_num, bool enabled);
typedef void (*board_set_ignition)(bool enabled);
typedef void (*board_set_individual_ignition)(uint8_t bitmask);
typedef void (*board_set_harness_orientation)(uint8_t orientation);
@@ -23,6 +24,7 @@ struct board {
board_board_tick board_tick;
board_get_button get_button;
board_set_panda_power set_panda_power;
board_set_panda_individual_power set_panda_individual_power;
board_set_ignition set_ignition;
board_set_individual_ignition set_individual_ignition;
board_set_harness_orientation set_harness_orientation;
@@ -75,3 +77,8 @@ void unused_board_enable_header_pin(uint8_t pin_num, bool enabled) {
UNUSED(pin_num);
UNUSED(enabled);
}
void unused_set_panda_individual_power(uint8_t port_num, bool enabled) {
UNUSED(port_num);
UNUSED(enabled);
}
+1
View File
@@ -164,6 +164,7 @@ const board board_v1 = {
.board_tick = &board_v1_tick,
.get_button = &board_v1_get_button,
.set_panda_power = &board_v1_set_panda_power,
.set_panda_individual_power = &unused_set_panda_individual_power,
.set_ignition = &board_v1_set_ignition,
.set_individual_ignition = &unused_set_individual_ignition,
.set_harness_orientation = &board_v1_set_harness_orientation,
+18
View File
@@ -178,9 +178,26 @@ void board_v2_set_can_mode(uint8_t mode) {
}
bool panda_power = false;
uint8_t panda_power_bitmask = 0U;
void board_v2_set_panda_power(bool enable) {
panda_power = enable;
gpio_set_all_output(power_pins, sizeof(power_pins) / sizeof(gpio_t), enable);
if (enable) {
panda_power_bitmask = 0xFFU;
} else {
panda_power_bitmask = 0U;
}
}
void board_v2_set_panda_individual_power(uint8_t port_num, bool enable) {
port_num -= 1U;
if (port_num < 6U) {
panda_power_bitmask &= ~(1U << port_num);
panda_power_bitmask |= (enable ? 1U : 0U) << port_num;
} else {
print("Invalid port number ("); puth(port_num); print("): enabling failed\n");
}
gpio_set_bitmask(power_pins, sizeof(power_pins) / sizeof(gpio_t), (uint32_t)panda_power_bitmask);
}
bool board_v2_get_button(void) {
@@ -297,6 +314,7 @@ const board board_v2 = {
.board_tick = &board_v2_tick,
.get_button = &board_v2_get_button,
.set_panda_power = &board_v2_set_panda_power,
.set_panda_individual_power = &board_v2_set_panda_individual_power,
.set_ignition = &board_v2_set_ignition,
.set_individual_ignition = &board_v2_set_individual_ignition,
.set_harness_orientation = &board_v2_set_harness_orientation,
+6
View File
@@ -141,6 +141,9 @@ int main(void) {
clock_init();
peripherals_init();
detect_board_type();
// red+green leds enabled until succesful USB init, as a debug indicator
current_board->set_led(LED_RED, true);
current_board->set_led(LED_GREEN, true);
// print hello
print("\n\n\n************************ MAIN START ************************\n");
@@ -172,6 +175,9 @@ int main(void) {
// enable USB (right before interrupts or enum can fail!)
usb_init();
current_board->set_led(LED_RED, false);
current_board->set_led(LED_GREEN, false);
print("**** INTERRUPTS ON ****\n");
enable_interrupts();
+5
View File
@@ -58,6 +58,10 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
case 0xa2:
current_board->set_ignition((req->param1 == 1U));
break;
// **** 0xa0: Set panda power per channel by bitmask.
case 0xa3:
current_board->set_panda_individual_power(req->param1, (req->param2 > 0U));
break;
// **** 0xa8: get microsecond timer
case 0xa8:
time = microsecond_timer_get();
@@ -80,6 +84,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
case 0xc2:
COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE);
if (req->param1 < 3U) {
update_can_health_pkt(req->param1, 0U);
can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U);
can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U);
can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
const uint8_t gitversion[] = "DEV-bbab69eb-DEBUG";
const uint8_t gitversion[] = "DEV-b6dc9e48-DEBUG";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
DEV-bbab69eb-DEBUG
DEV-b6dc9e48-DEBUG
+3 -3
View File
@@ -266,7 +266,7 @@ int main(void) {
REGISTER_INTERRUPT(CAN1_TX_IRQn, CAN1_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
REGISTER_INTERRUPT(CAN1_RX0_IRQn, CAN1_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
REGISTER_INTERRUPT(CAN1_SCE_IRQn, CAN1_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
// Should run at around 732Hz (see init below)
REGISTER_INTERRUPT(TIM3_IRQn, TIM3_IRQ_Handler, 1000U, FAULT_INTERRUPT_RATE_TIM3)
@@ -290,12 +290,12 @@ int main(void) {
adc_init();
// init can
bool llcan_speed_set = llcan_set_speed(CAN1, 5000, false, false);
bool llcan_speed_set = llcan_set_speed(CAN, 5000, false, false);
if (!llcan_speed_set) {
print("Failed to set llcan speed");
}
bool ret = llcan_init(CAN1);
bool ret = llcan_init(CAN);
UNUSED(ret);
// 48mhz / 65536 ~= 732
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+23 -61
View File
@@ -8,7 +8,7 @@ import hashlib
import binascii
import datetime
import logging
from functools import wraps
from functools import wraps, partial
from typing import Optional
from itertools import accumulate
@@ -90,53 +90,21 @@ def unpack_can_buffer(dat):
return (ret, dat)
def ensure_health_packet_version(fn):
def ensure_version(desc, lib_field, panda_field, fn):
@wraps(fn)
def wrapper(self, *args, **kwargs):
if self.health_version < self.HEALTH_PACKET_VERSION:
raise RuntimeError("Panda firmware has outdated health packet definition. Reflash panda firmware.")
elif self.health_version > self.HEALTH_PACKET_VERSION:
raise RuntimeError("Panda python library has outdated health packet definition. Update panda python library.")
lib_version = getattr(self, lib_field)
panda_version = getattr(self, panda_field)
if lib_version != panda_version:
raise RuntimeError(f"{desc} packet version mismatch: panda's firmware v{panda_version}, library v{lib_version}. Reflash panda.")
return fn(self, *args, **kwargs)
return wrapper
ensure_can_packet_version = partial(ensure_version, "CAN", "CAN_PACKET_VERSION", "can_version")
ensure_can_health_packet_version = partial(ensure_version, "CAN health", "CAN_HEALTH_PACKET_VERSION", "can_health_version")
ensure_health_packet_version = partial(ensure_version, "health", "HEALTH_PACKET_VERSION", "health_version")
def ensure_can_packet_version(fn):
@wraps(fn)
def wrapper(self, *args, **kwargs):
if self.can_version < self.CAN_PACKET_VERSION:
raise RuntimeError("Panda firmware has outdated CAN packet definition. Reflash panda firmware.")
elif self.can_version > self.CAN_PACKET_VERSION:
raise RuntimeError("Panda python library has outdated CAN packet definition. Update panda python library.")
return fn(self, *args, **kwargs)
return wrapper
def ensure_can_health_packet_version(fn):
@wraps(fn)
def wrapper(self, *args, **kwargs):
if self.can_health_version < self.CAN_HEALTH_PACKET_VERSION:
raise RuntimeError("Panda firmware has outdated CAN health packet definition. Reflash panda firmware.")
elif self.can_health_version > self.CAN_HEALTH_PACKET_VERSION:
raise RuntimeError("Panda python library has outdated CAN health packet definition. Update panda python library.")
return fn(self, *args, **kwargs)
return wrapper
def parse_timestamp(dat):
a = struct.unpack("HBBBBBB", dat)
if a[0] == 0:
return None
try:
return datetime.datetime(a[0], a[1], a[2], a[4], a[5], a[6])
except ValueError:
return None
def unpack_log(dat):
return {
'id': struct.unpack("H", dat[:2])[0],
'timestamp': parse_timestamp(dat[2:10]),
'uptime': struct.unpack("I", dat[10:14])[0],
'msg': bytes(dat[14:]).decode('utf-8', 'ignore').strip('\x00'),
}
class ALTERNATIVE_EXPERIENCE:
DEFAULT = 0
@@ -239,6 +207,7 @@ class Panda:
FLAG_HYUNDAI_CANFD_HDA2 = 16
FLAG_HYUNDAI_CANFD_ALT_BUTTONS = 32
FLAG_HYUNDAI_ALT_LIMITS = 64
FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING = 128
FLAG_TESLA_POWERTRAIN = 1
FLAG_TESLA_LONG_CONTROL = 2
@@ -251,6 +220,8 @@ class Panda:
FLAG_SUBARU_GEN2 = 1
FLAG_SUBARU_LONG = 2
FLAG_NISSAN_ALT_EPS_BUS = 1
FLAG_GM_HW_CAM = 1
FLAG_GM_HW_CAM_LONG = 2
@@ -281,6 +252,8 @@ class Panda:
if self._handle_open:
self._handle.close()
self._handle_open = False
if self._context is not None:
self._context.close()
def connect(self, claim=True, wait=False):
self.close()
@@ -288,9 +261,9 @@ class Panda:
self._handle = None
while self._handle is None:
# try USB first, then SPI
self._handle, serial, self.bootstub, bcd = self.usb_connect(self._connect_serial, claim=claim)
self._context, self._handle, serial, self.bootstub, bcd = self.usb_connect(self._connect_serial, claim=claim)
if self._handle is None:
self._handle, serial, self.bootstub, bcd = self.spi_connect(self._connect_serial)
self._context, self._handle, serial, self.bootstub, bcd = self.spi_connect(self._connect_serial)
if not wait:
break
@@ -366,7 +339,7 @@ class Panda:
err = f"panda protocol mismatch: expected {handle.PROTOCOL_VERSION}, got {spi_version}. reflash panda"
raise PandaProtocolMismatch(err)
return handle, spi_serial, bootstub, None
return None, handle, spi_serial, bootstub, None
@classmethod
def usb_connect(cls, serial, claim=True):
@@ -408,7 +381,7 @@ class Panda:
else:
context.close()
return usb_handle, usb_serial, bootstub, bcd
return context, usb_handle, usb_serial, bootstub, bcd
@classmethod
def list(cls): # noqa: A003
@@ -437,7 +410,7 @@ class Panda:
@classmethod
def spi_list(cls):
_, serial, _, _ = cls.spi_connect(None, ignore_version=True)
_, _, serial, _, _ = cls.spi_connect(None, ignore_version=True)
if serial is not None:
return [serial, ]
return []
@@ -468,7 +441,7 @@ class Panda:
success = False
# wait up to 15 seconds
for _ in range(0, 15*10):
for _ in range(15*10):
try:
self.connect()
success = True
@@ -976,7 +949,8 @@ class Panda:
def get_datetime(self):
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xa0, 0, 0, 8)
return parse_timestamp(dat)
a = struct.unpack("HBBBBBB", dat)
return datetime.datetime(a[0], a[1], a[2], a[4], a[5], a[6])
# ****************** Timer *****************
def get_microsecond_timer(self):
@@ -1013,15 +987,3 @@ class Panda:
def force_relay_drive(self, intercept_relay_drive, ignition_relay_drive):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xc5, (int(intercept_relay_drive) | int(ignition_relay_drive) << 1), 0, b'')
# ****************** Logging *****************
def get_logs(self, last_id=None, get_all=False):
assert (last_id is None) or (0 <= last_id < 0xFFFF)
logs = []
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xfd, 1 if get_all else 0, last_id if last_id is not None else 0xFFFF, 0x40)
while len(dat) > 0:
if len(dat) == 0x40:
logs.append(unpack_log(dat))
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xfd, 0, 0xFFFF, 0x40)
return logs
+1 -6
View File
@@ -53,14 +53,9 @@ class BaseSTBootloaderHandle(ABC):
...
@abstractmethod
def erase_app(self) -> None:
...
@abstractmethod
def erase_bootstub(self) -> None:
def erase_sector(self, sector: int) -> None:
...
@abstractmethod
def jump(self, address: int) -> None:
...
-1
View File
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
import sys
import time
import struct
+10 -5
View File
@@ -10,35 +10,40 @@ USBPACKET_MAX_SIZE = 0x40
class McuConfig(NamedTuple):
mcu: str
mcu_idcode: int
sector_sizes: List[int]
sector_count: int # total sector count, used for MCU identification in DFU mode
uid_address: int
block_size: int
sector_sizes: List[int]
serial_number_address: int
app_address: int
app_fn: str
bootstub_address: int
bootstub_fn: str
def sector_address(self, i):
# assume bootstub is in sector 0
return self.bootstub_address + sum(self.sector_sizes[:i])
Fx = (
0x1FFF7A10,
0x800,
[0x4000 for _ in range(4)] + [0x10000] + [0x20000 for _ in range(11)],
0x1FFF79C0,
0x8004000,
"panda.bin.signed",
0x8000000,
"bootstub.panda.bin",
)
F2Config = McuConfig("STM32F2", 0x411, *Fx)
F4Config = McuConfig("STM32F4", 0x463, *Fx)
F2Config = McuConfig("STM32F2", 0x411, [0x4000 for _ in range(4)] + [0x10000] + [0x20000 for _ in range(7)], 12, *Fx)
F4Config = McuConfig("STM32F4", 0x463, [0x4000 for _ in range(4)] + [0x10000] + [0x20000 for _ in range(11)], 16, *Fx)
H7Config = McuConfig(
"STM32H7",
0x483,
[0x20000 for _ in range(7)],
8,
0x1FF1E800,
0x400,
# there is an 8th sector, but we use that for the provisioning chunk, so don't program over that!
[0x20000 for _ in range(7)],
0x080FFFC0,
0x8020000,
"panda_h7.bin.signed",
+25 -9
View File
@@ -14,9 +14,9 @@ class PandaDFU:
def __init__(self, dfu_serial: Optional[str]):
# try USB, then SPI
handle: Optional[BaseSTBootloaderHandle]
handle = PandaDFU.usb_connect(dfu_serial)
self._context, handle = PandaDFU.usb_connect(dfu_serial)
if handle is None:
handle = PandaDFU.spi_connect(dfu_serial)
self._context, handle = PandaDFU.spi_connect(dfu_serial)
if handle is None:
raise Exception(f"failed to open DFU device {dfu_serial}")
@@ -24,8 +24,21 @@ class PandaDFU:
self._handle: BaseSTBootloaderHandle = handle
self._mcu_type: McuType = self._handle.get_mcu_type()
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
if self._handle is not None:
self._handle.close()
self._handle = None
if self._context is not None:
self._context.close()
@staticmethod
def usb_connect(dfu_serial: Optional[str]) -> Optional[STBootloaderUSBHandle]:
def usb_connect(dfu_serial: Optional[str]):
handle = None
context = usb1.USBContext()
context.open()
@@ -40,10 +53,10 @@ class PandaDFU:
handle = STBootloaderUSBHandle(device, device.open())
break
return handle
return context, handle
@staticmethod
def spi_connect(dfu_serial: Optional[str]) -> Optional[STBootloaderSPIHandle]:
def spi_connect(dfu_serial: Optional[str]):
handle = None
this_dfu_serial = None
@@ -56,7 +69,7 @@ class PandaDFU:
if dfu_serial is not None and dfu_serial != this_dfu_serial:
handle = None
return handle
return None, handle
@staticmethod
def list() -> List[str]: # noqa: A003
@@ -82,7 +95,7 @@ class PandaDFU:
@staticmethod
def spi_list() -> List[str]:
try:
h = PandaDFU.spi_connect(None)
_, h = PandaDFU.spi_connect(None)
if h is not None:
dfu_serial = PandaDFU.st_serial_to_dfu_serial(h.get_uid(), h.get_mcu_type())
return [dfu_serial, ]
@@ -108,8 +121,11 @@ class PandaDFU:
def program_bootstub(self, code_bootstub):
self._handle.clear_status()
self._handle.erase_bootstub()
self._handle.erase_app()
# erase all sectors
for i in range(len(self._mcu_type.config.sector_sizes)):
self._handle.erase_sector(i)
self._handle.program(self._mcu_type.config.bootstub_address, code_bootstub)
def recover(self):
+2 -9
View File
@@ -17,10 +17,8 @@ from .utils import crc8_pedal
try:
import spidev
import spidev2
except ImportError:
spidev = None
spidev2 = None
# Constants
SYNC = 0x5A
@@ -194,6 +192,7 @@ class PandaSpiHandle(BaseHandle):
return dat[3:-1]
def _transfer_kernel_driver(self, spi, endpoint: int, data, timeout: int, max_rx_len: int = 1000, expect_disconnect: bool = False) -> bytes:
import spidev2
self.tx_buf[:len(data)] = data
self.ioctl_data.endpoint = endpoint
self.ioctl_data.tx_length = len(data)
@@ -403,12 +402,6 @@ class STBootloaderSPIHandle(BaseSTBootloaderHandle):
# *** PandaDFU API ***
def erase_app(self):
self.erase_sector(1)
def erase_bootstub(self):
self.erase_sector(0)
def get_mcu_type(self):
return self._mcu_type
@@ -421,7 +414,7 @@ class STBootloaderSPIHandle(BaseSTBootloaderHandle):
def program(self, address, dat):
bs = 256 # max block size for writing to flash over SPI
dat += b"\xFF" * ((bs - len(dat)) % bs)
for i in range(0, len(dat) // bs):
for i in range(len(dat) // bs):
block = dat[i * bs:(i + 1) * bs]
self._cmd(0x31, data=[
struct.pack('>I', address + i*bs),
-1
View File
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
import time
import struct
from collections import deque
+13 -9
View File
@@ -34,9 +34,16 @@ class STBootloaderUSBHandle(BaseSTBootloaderHandle):
def __init__(self, libusb_device, libusb_handle):
self._libusb_handle = libusb_handle
# TODO: Find a way to detect F4 vs F2
# TODO: also check F4 BCD, don't assume in else
self._mcu_type = McuType.H7 if libusb_device.getbcdDevice() == 512 else McuType.F4
# example from F4: lsusb -v | grep Flash
# iInterface 4 @Internal Flash /0x08000000/04*016Kg,01*064Kg,011*128Kg
for i in range(20):
desc = libusb_handle.getStringDescriptor(i, 0)
if desc is not None and desc.startswith("@Internal Flash"):
sector_count = sum([int(s.split('*')[0]) for s in desc.split('/')[-1].split(',')])
break
mcu_by_sector_count = {m.config.sector_count: m for m in McuType}
assert sector_count in mcu_by_sector_count, f"Unkown MCU: {sector_count=}"
self._mcu_type = mcu_by_sector_count[sector_count]
def _status(self) -> None:
while 1:
@@ -51,11 +58,8 @@ class STBootloaderUSBHandle(BaseSTBootloaderHandle):
def get_mcu_type(self):
return self._mcu_type
def erase_app(self):
self._erase_page_address(self._mcu_type.config.app_address)
def erase_bootstub(self):
self._erase_page_address(self._mcu_type.config.bootstub_address)
def erase_sector(self, sector: int):
self._erase_page_address(self._mcu_type.config.sector_address(sector))
def clear_status(self):
# Clear status
@@ -78,7 +82,7 @@ class STBootloaderUSBHandle(BaseSTBootloaderHandle):
# Program
bs = min(len(dat), self._mcu_type.config.block_size)
dat += b"\xFF" * ((bs - len(dat)) % bs)
for i in range(0, len(dat) // bs):
for i in range(len(dat) // bs):
ldat = dat[i * bs:(i + 1) * bs]
print("programming %d with length %d" % (i, len(ldat)))
self._libusb_handle.controlWrite(0x21, self.DFU_DNLOAD, 2 + i, 0, ldat)