Merge panda subtree

old-commit-hash: df875819f5
commatwo_master
Vehicle Researcher 5 years ago
commit 0c606ee339
  1. 2
      panda/.circleci/config.yml
  2. 2
      panda/Dockerfile
  3. 5
      panda/Jenkinsfile
  4. 2
      panda/README.md
  5. 2
      panda/VERSION
  6. 4
      panda/board/board.h
  7. 4
      panda/board/board_declarations.h
  8. 14
      panda/board/boards/black.h
  9. 2
      panda/board/boards/common.h
  10. 13
      panda/board/boards/grey.h
  11. 11
      panda/board/boards/pedal.h
  12. 56
      panda/board/boards/uno.h
  13. 53
      panda/board/boards/white.h
  14. 17
      panda/board/bootstub.c
  15. 4
      panda/board/build.mk
  16. 4
      panda/board/config.h
  17. 23
      panda/board/critical.h
  18. 22
      panda/board/drivers/adc.h
  19. 70
      panda/board/drivers/can.h
  20. 24
      panda/board/drivers/clock.h
  21. 17
      panda/board/drivers/dac.h
  22. 29
      panda/board/drivers/fan.h
  23. 20
      panda/board/drivers/gmlan_alt.h
  24. 31
      panda/board/drivers/harness.h
  25. 164
      panda/board/drivers/interrupts.h
  26. 36
      panda/board/drivers/llcan.h
  27. 14
      panda/board/drivers/llgpio.h
  28. 31
      panda/board/drivers/pwm.h
  29. 81
      panda/board/drivers/registers.h
  30. 20
      panda/board/drivers/rtc.h
  31. 86
      panda/board/drivers/spi.h
  32. 6
      panda/board/drivers/timer.h
  33. 17
      panda/board/drivers/uart.h
  34. 156
      panda/board/drivers/usb.h
  35. 49
      panda/board/faults.h
  36. 10
      panda/board/gpio.h
  37. 4
      panda/board/inc/stm32f413xx.h
  38. 25
      panda/board/libc.h
  39. 316
      panda/board/main.c
  40. 3
      panda/board/main_declarations.h
  41. 26
      panda/board/pedal/main.c
  42. 3
      panda/board/power_saving.h
  43. 36
      panda/board/safety.h
  44. 13
      panda/board/safety/safety_cadillac.h
  45. 54
      panda/board/safety/safety_chrysler.h
  46. 6
      panda/board/safety/safety_defaults.h
  47. 2
      panda/board/safety/safety_elm327.h
  48. 14
      panda/board/safety/safety_ford.h
  49. 48
      panda/board/safety/safety_gm.h
  50. 86
      panda/board/safety/safety_honda.h
  51. 60
      panda/board/safety/safety_hyundai.h
  52. 91
      panda/board/safety/safety_mazda.h
  53. 44
      panda/board/safety/safety_subaru.h
  54. 40
      panda/board/safety/safety_toyota.h
  55. 78
      panda/board/safety/safety_volkswagen.h
  56. 20
      panda/board/safety_declarations.h
  57. 17
      panda/board/spi_flasher.h
  58. 6
      panda/crypto/sign.py
  59. 4
      panda/drivers/linux/panda.c
  60. 2
      panda/drivers/windows/panda_shared/panda.cpp
  61. 2
      panda/drivers/windows/panda_shared/panda.h
  62. 9
      panda/examples/can_logger.py
  63. 59
      panda/examples/eps_read_software_ids.py
  64. 77
      panda/examples/query_fw_versions.py
  65. 4
      panda/examples/tesla_tester.py
  66. 57
      panda/python/__init__.py
  67. 188
      panda/python/uds.py
  68. 4
      panda/requirements.txt
  69. 25
      panda/tests/automated/1_program.py
  70. 44
      panda/tests/automated/2_health.py
  71. 26
      panda/tests/automated/3_usb_to_can.py
  72. 6
      panda/tests/automated/4_wifi.py
  73. 13
      panda/tests/automated/5_wifi_functionality.py
  74. 195
      panda/tests/automated/6_two_panda.py
  75. 11
      panda/tests/automated/6_wifi_udp.py
  76. 202
      panda/tests/automated/7_can_loopback.py
  77. 213
      panda/tests/automated/helpers.py
  78. 25
      panda/tests/automated/timeout.py
  79. 85
      panda/tests/automated/wifi_helpers.py
  80. 2
      panda/tests/black_white_loopback_test.py
  81. 2
      panda/tests/black_white_relay_endurance.py
  82. 2
      panda/tests/black_white_relay_test.py
  83. 2
      panda/tests/debug_console.py
  84. 50
      panda/tests/development/register_hashmap_spread.py
  85. 35
      panda/tests/echo.py
  86. 2
      panda/tests/ir_test.py
  87. 1
      panda/tests/misra/.gitignore
  88. 2
      panda/tests/misra/coverage_table
  89. 2
      panda/tests/misra/test_misra.sh
  90. 36
      panda/tests/safety/common.py
  91. 8
      panda/tests/safety/libpandasafety_py.py
  92. 27
      panda/tests/safety/test.c
  93. 43
      panda/tests/safety/test_cadillac.py
  94. 95
      panda/tests/safety/test_chrysler.py
  95. 56
      panda/tests/safety/test_gm.py
  96. 61
      panda/tests/safety/test_honda.py
  97. 24
      panda/tests/safety/test_honda_bosch.py
  98. 99
      panda/tests/safety/test_hyundai.py
  99. 43
      panda/tests/safety/test_subaru.py
  100. 93
      panda/tests/safety/test_toyota.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -64,7 +64,7 @@ jobs:
- run: - run:
name: Build ESP image name: Build ESP image
command: | command: |
docker run panda_build /bin/bash -c "cd /panda/boardesp; make user1.bin" docker run panda_build /bin/bash -c "cd /panda/boardesp; ./get_sdk.sh; make user1.bin"
safety_replay: safety_replay:
machine: machine:

@ -71,6 +71,8 @@ ENV PYTHONPATH /tmp:$PYTHONPATH
COPY ./boardesp/get_sdk_ci.sh /tmp/panda/boardesp/ COPY ./boardesp/get_sdk_ci.sh /tmp/panda/boardesp/
COPY ./boardesp/python2_make.py /tmp/panda/boardesp/ COPY ./boardesp/python2_make.py /tmp/panda/boardesp/
COPY ./panda_jungle /tmp/panda_jungle
RUN useradd --system -s /sbin/nologin pandauser RUN useradd --system -s /sbin/nologin pandauser
RUN mkdir -p /tmp/panda/boardesp/esp-open-sdk RUN mkdir -p /tmp/panda/boardesp/esp-open-sdk
RUN chown pandauser /tmp/panda/boardesp/esp-open-sdk RUN chown pandauser /tmp/panda/boardesp/esp-open-sdk

5
panda/Jenkinsfile vendored

@ -14,6 +14,11 @@ pipeline {
steps { steps {
timeout(time: 60, unit: 'MINUTES') { timeout(time: 60, unit: 'MINUTES') {
script { script {
try {
sh 'cp -R /home/batman/panda_jungle .'
} catch (err) {
echo "Folder already exists"
}
sh 'git archive -v -o panda.tar.gz --format=tar.gz HEAD' sh 'git archive -v -o panda.tar.gz --format=tar.gz HEAD'
dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}") dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}")
} }

@ -87,7 +87,7 @@ To print out the serial console from the ESP8266, run PORT=1 tests/debug_console
Safety Model Safety Model
------ ------
When a panda powers up, by default it's in `SAFETY_NOOUTPUT` mode. While in no output mode, the buses are also forced to be silent. In order to send messages, you have to select a safety mode. Currently, setting safety modes is only supported over USB. When a panda powers up, by default it's in `SAFETY_SILENT` mode. While in `SAFETY_SILENT` mode, the buses are also forced to be silent. In order to send messages, you have to select a safety mode. Currently, setting safety modes is only supported over USB.
Safety modes optionally supports `controls_allowed`, which allows or blocks a subset of messages based on a customizable state in the board. Safety modes optionally supports `controls_allowed`, which allows or blocks a subset of messages based on a customizable state in the board.

@ -1 +1 @@
v1.5.9 v1.6.9

@ -19,7 +19,9 @@
void detect_board_type(void) { void detect_board_type(void) {
#ifdef PANDA #ifdef PANDA
// SPI lines floating: white (TODO: is this reliable?) // SPI lines floating: white (TODO: is this reliable? Not really, we have to enable ESP/GPS to be able to detect this on the UART)
set_gpio_output(GPIOC, 14, 1);
set_gpio_output(GPIOC, 5, 1);
if((detect_with_pull(GPIOA, 4, PULL_DOWN)) || (detect_with_pull(GPIOA, 5, PULL_DOWN)) || (detect_with_pull(GPIOA, 6, PULL_DOWN)) || (detect_with_pull(GPIOA, 7, PULL_DOWN))){ if((detect_with_pull(GPIOA, 4, PULL_DOWN)) || (detect_with_pull(GPIOA, 5, PULL_DOWN)) || (detect_with_pull(GPIOA, 6, PULL_DOWN)) || (detect_with_pull(GPIOA, 7, PULL_DOWN))){
hw_type = HW_TYPE_WHITE_PANDA; hw_type = HW_TYPE_WHITE_PANDA;
current_board = &board_white; current_board = &board_white;

@ -6,11 +6,12 @@ typedef void (*board_set_led)(uint8_t color, bool enabled);
typedef void (*board_set_usb_power_mode)(uint8_t mode); typedef void (*board_set_usb_power_mode)(uint8_t mode);
typedef void (*board_set_esp_gps_mode)(uint8_t mode); typedef void (*board_set_esp_gps_mode)(uint8_t mode);
typedef void (*board_set_can_mode)(uint8_t mode); typedef void (*board_set_can_mode)(uint8_t mode);
typedef void (*board_usb_power_mode_tick)(uint64_t tcnt); typedef void (*board_usb_power_mode_tick)(uint32_t uptime);
typedef bool (*board_check_ignition)(void); typedef bool (*board_check_ignition)(void);
typedef uint32_t (*board_read_current)(void); typedef uint32_t (*board_read_current)(void);
typedef void (*board_set_ir_power)(uint8_t percentage); typedef void (*board_set_ir_power)(uint8_t percentage);
typedef void (*board_set_fan_power)(uint8_t percentage); typedef void (*board_set_fan_power)(uint8_t percentage);
typedef void (*board_set_phone_power)(bool enabled);
struct board { struct board {
const char *board_type; const char *board_type;
@ -27,6 +28,7 @@ struct board {
board_read_current read_current; board_read_current read_current;
board_set_ir_power set_ir_power; board_set_ir_power set_ir_power;
board_set_fan_power set_fan_power; board_set_fan_power set_fan_power;
board_set_phone_power set_phone_power;
}; };
// ******************* Definitions ******************** // ******************* Definitions ********************

@ -123,8 +123,8 @@ void black_set_can_mode(uint8_t mode){
} }
} }
void black_usb_power_mode_tick(uint64_t tcnt){ void black_usb_power_mode_tick(uint32_t uptime){
UNUSED(tcnt); UNUSED(uptime);
// Not applicable // Not applicable
} }
@ -146,6 +146,10 @@ void black_set_fan_power(uint8_t percentage){
UNUSED(percentage); UNUSED(percentage);
} }
void black_set_phone_power(bool enabled){
UNUSED(enabled);
}
void black_init(void) { void black_init(void) {
common_init_gpio(); common_init_gpio();
@ -158,6 +162,9 @@ void black_init(void) {
set_gpio_mode(GPIOC, 0, MODE_ANALOG); set_gpio_mode(GPIOC, 0, MODE_ANALOG);
set_gpio_mode(GPIOC, 3, MODE_ANALOG); set_gpio_mode(GPIOC, 3, MODE_ANALOG);
// Set default state of GPS
current_board->set_esp_gps_mode(ESP_GPS_ENABLED);
// C10: OBD_SBU1_RELAY (harness relay driving output) // C10: OBD_SBU1_RELAY (harness relay driving output)
// C11: OBD_SBU2_RELAY (harness relay driving output) // C11: OBD_SBU2_RELAY (harness relay driving output)
set_gpio_mode(GPIOC, 10, MODE_OUTPUT); set_gpio_mode(GPIOC, 10, MODE_OUTPUT);
@ -227,5 +234,6 @@ const board board_black = {
.check_ignition = black_check_ignition, .check_ignition = black_check_ignition,
.read_current = black_read_current, .read_current = black_read_current,
.set_fan_power = black_set_fan_power, .set_fan_power = black_set_fan_power,
.set_ir_power = black_set_ir_power .set_ir_power = black_set_ir_power,
.set_phone_power = black_set_phone_power
}; };

@ -61,7 +61,7 @@ void peripherals_init(void){
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // pedal and fan PWM RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // pedal and fan PWM
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // gmlan_alt and IR PWM RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // gmlan_alt and IR PWM
//RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; //RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
//RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // interrupt timer
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN;

@ -3,10 +3,18 @@
// ////////// // // ////////// //
// Most hardware functionality is similar to white panda // Most hardware functionality is similar to white panda
void grey_init(void) {
white_grey_common_init();
// Set default state of GPS
current_board->set_esp_gps_mode(ESP_GPS_ENABLED);
}
const board board_grey = { const board board_grey = {
.board_type = "Grey", .board_type = "Grey",
.harness_config = &white_harness_config, .harness_config = &white_harness_config,
.init = white_init, .init = grey_init,
.enable_can_transciever = white_enable_can_transciever, .enable_can_transciever = white_enable_can_transciever,
.enable_can_transcievers = white_enable_can_transcievers, .enable_can_transcievers = white_enable_can_transcievers,
.set_led = white_set_led, .set_led = white_set_led,
@ -17,5 +25,6 @@ const board board_grey = {
.check_ignition = white_check_ignition, .check_ignition = white_check_ignition,
.read_current = white_read_current, .read_current = white_read_current,
.set_fan_power = white_set_fan_power, .set_fan_power = white_set_fan_power,
.set_ir_power = white_set_ir_power .set_ir_power = white_set_ir_power,
.set_phone_power = white_set_phone_power
}; };

@ -50,8 +50,8 @@ void pedal_set_can_mode(uint8_t mode){
} }
} }
void pedal_usb_power_mode_tick(uint64_t tcnt){ void pedal_usb_power_mode_tick(uint32_t uptime){
UNUSED(tcnt); UNUSED(uptime);
// Not applicable // Not applicable
} }
@ -73,6 +73,10 @@ void pedal_set_fan_power(uint8_t percentage){
UNUSED(percentage); UNUSED(percentage);
} }
void pedal_set_phone_power(bool enabled){
UNUSED(enabled);
}
void pedal_init(void) { void pedal_init(void) {
common_init_gpio(); common_init_gpio();
@ -108,5 +112,6 @@ const board board_pedal = {
.check_ignition = pedal_check_ignition, .check_ignition = pedal_check_ignition,
.read_current = pedal_read_current, .read_current = pedal_read_current,
.set_fan_power = pedal_set_fan_power, .set_fan_power = pedal_set_fan_power,
.set_ir_power = pedal_set_ir_power .set_ir_power = pedal_set_ir_power,
.set_phone_power = pedal_set_phone_power
}; };

@ -1,6 +1,8 @@
// ///////////// // // ///////////// //
// Uno + Harness // // Uno + Harness //
// ///////////// // // ///////////// //
#define BOOTKICK_TIME 3U
uint8_t bootkick_timer = 0U;
void uno_enable_can_transciever(uint8_t transciever, bool enabled) { void uno_enable_can_transciever(uint8_t transciever, bool enabled) {
switch (transciever){ switch (transciever){
@ -48,9 +50,38 @@ void uno_set_gps_load_switch(bool enabled) {
set_gpio_output(GPIOC, 12, enabled); set_gpio_output(GPIOC, 12, enabled);
} }
void uno_set_bootkick(bool enabled){
set_gpio_output(GPIOB, 14, !enabled);
}
void uno_bootkick(void) {
bootkick_timer = BOOTKICK_TIME;
uno_set_bootkick(true);
}
void uno_set_phone_power(bool enabled){
set_gpio_output(GPIOB, 4, enabled);
}
void uno_set_usb_power_mode(uint8_t mode) { void uno_set_usb_power_mode(uint8_t mode) {
UNUSED(mode); bool valid = false;
puts("Setting USB mode makes no sense on UNO\n"); switch (mode) {
case USB_POWER_CLIENT:
uno_set_phone_power(false);
valid = true;
break;
case USB_POWER_CDP:
uno_set_phone_power(true);
uno_bootkick();
valid = true;
break;
default:
puts("Invalid USB power mode\n");
break;
}
if (valid) {
usb_power_mode = mode;
}
} }
void uno_set_esp_gps_mode(uint8_t mode) { void uno_set_esp_gps_mode(uint8_t mode) {
@ -106,12 +137,11 @@ void uno_set_can_mode(uint8_t mode){
} }
} }
void uno_set_bootkick(bool enabled){ void uno_usb_power_mode_tick(uint32_t uptime){
set_gpio_output(GPIOB, 14, !enabled); UNUSED(uptime);
} if(bootkick_timer != 0U){
bootkick_timer--;
void uno_usb_power_mode_tick(uint64_t tcnt){ } else {
if(tcnt == 3U){
uno_set_bootkick(false); uno_set_bootkick(false);
} }
} }
@ -152,6 +182,9 @@ void uno_init(void) {
set_gpio_mode(GPIOC, 0, MODE_ANALOG); set_gpio_mode(GPIOC, 0, MODE_ANALOG);
set_gpio_mode(GPIOC, 3, MODE_ANALOG); set_gpio_mode(GPIOC, 3, MODE_ANALOG);
// Set default state of GPS
current_board->set_esp_gps_mode(ESP_GPS_ENABLED);
// C10: OBD_SBU1_RELAY (harness relay driving output) // C10: OBD_SBU1_RELAY (harness relay driving output)
// C11: OBD_SBU2_RELAY (harness relay driving output) // C11: OBD_SBU2_RELAY (harness relay driving output)
set_gpio_mode(GPIOC, 10, MODE_OUTPUT); set_gpio_mode(GPIOC, 10, MODE_OUTPUT);
@ -168,7 +201,7 @@ void uno_init(void) {
uno_set_gps_load_switch(true); uno_set_gps_load_switch(true);
// Turn on phone regulator // Turn on phone regulator
set_gpio_output(GPIOB, 4, 1); uno_set_phone_power(true);
// Initialize IR PWM and set to 0% // Initialize IR PWM and set to 0%
set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4); set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4);
@ -212,7 +245,7 @@ void uno_init(void) {
} }
// Bootkick phone // Bootkick phone
uno_set_bootkick(true); uno_bootkick();
} }
const harness_configuration uno_harness_config = { const harness_configuration uno_harness_config = {
@ -243,5 +276,6 @@ const board board_uno = {
.check_ignition = uno_check_ignition, .check_ignition = uno_check_ignition,
.read_current = uno_read_current, .read_current = uno_read_current,
.set_fan_power = uno_set_fan_power, .set_fan_power = uno_set_fan_power,
.set_ir_power = uno_set_ir_power .set_ir_power = uno_set_ir_power,
.set_phone_power = uno_set_phone_power
}; };

@ -78,11 +78,13 @@ void white_set_esp_gps_mode(uint8_t mode) {
set_gpio_output(GPIOC, 14, 0); set_gpio_output(GPIOC, 14, 0);
set_gpio_output(GPIOC, 5, 0); set_gpio_output(GPIOC, 5, 0);
break; break;
#ifndef EON
case ESP_GPS_ENABLED: case ESP_GPS_ENABLED:
// ESP ON // ESP ON
set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 14, 1);
set_gpio_output(GPIOC, 5, 1); set_gpio_output(GPIOC, 5, 1);
break; break;
#endif
case ESP_GPS_BOOTMODE: case ESP_GPS_BOOTMODE:
set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 14, 1);
set_gpio_output(GPIOC, 5, 0); set_gpio_output(GPIOC, 5, 0);
@ -156,8 +158,8 @@ uint32_t white_read_current(void){
return adc_get(ADCCHAN_CURRENT); return adc_get(ADCCHAN_CURRENT);
} }
uint64_t marker = 0; uint32_t marker = 0;
void white_usb_power_mode_tick(uint64_t tcnt){ void white_usb_power_mode_tick(uint32_t uptime){
// on EON or BOOTSTUB, no state machine // on EON or BOOTSTUB, no state machine
#if !defined(BOOTSTUB) && !defined(EON) #if !defined(BOOTSTUB) && !defined(EON)
@ -171,47 +173,47 @@ void white_usb_power_mode_tick(uint64_t tcnt){
switch (usb_power_mode) { switch (usb_power_mode) {
case USB_POWER_CLIENT: case USB_POWER_CLIENT:
if ((tcnt - marker) >= CLICKS) { if ((uptime - marker) >= CLICKS) {
if (!is_enumerated) { if (!is_enumerated) {
puts("USBP: didn't enumerate, switching to CDP mode\n"); puts("USBP: didn't enumerate, switching to CDP mode\n");
// switch to CDP // switch to CDP
white_set_usb_power_mode(USB_POWER_CDP); white_set_usb_power_mode(USB_POWER_CDP);
marker = tcnt; marker = uptime;
} }
} }
// keep resetting the timer if it's enumerated // keep resetting the timer if it's enumerated
if (is_enumerated) { if (is_enumerated) {
marker = tcnt; marker = uptime;
} }
break; break;
case USB_POWER_CDP: case USB_POWER_CDP:
// been CLICKS clicks since we switched to CDP // been CLICKS clicks since we switched to CDP
if ((tcnt-marker) >= CLICKS) { if ((uptime - marker) >= CLICKS) {
// measure current draw, if positive and no enumeration, switch to DCP // measure current draw, if positive and no enumeration, switch to DCP
if (!is_enumerated && (current < CURRENT_THRESHOLD)) { if (!is_enumerated && (current < CURRENT_THRESHOLD)) {
puts("USBP: no enumeration with current draw, switching to DCP mode\n"); puts("USBP: no enumeration with current draw, switching to DCP mode\n");
white_set_usb_power_mode(USB_POWER_DCP); white_set_usb_power_mode(USB_POWER_DCP);
marker = tcnt; marker = uptime;
} }
} }
// keep resetting the timer if there's no current draw in CDP // keep resetting the timer if there's no current draw in CDP
if (current >= CURRENT_THRESHOLD) { if (current >= CURRENT_THRESHOLD) {
marker = tcnt; marker = uptime;
} }
break; break;
case USB_POWER_DCP: case USB_POWER_DCP:
// been at least CLICKS clicks since we switched to DCP // been at least CLICKS clicks since we switched to DCP
if ((tcnt-marker) >= CLICKS) { if ((uptime - marker) >= CLICKS) {
// if no current draw, switch back to CDP // if no current draw, switch back to CDP
if (current >= CURRENT_THRESHOLD) { if (current >= CURRENT_THRESHOLD) {
puts("USBP: no current draw, switching back to CDP mode\n"); puts("USBP: no current draw, switching back to CDP mode\n");
white_set_usb_power_mode(USB_POWER_CDP); white_set_usb_power_mode(USB_POWER_CDP);
marker = tcnt; marker = uptime;
} }
} }
// keep resetting the timer if there's current draw in DCP // keep resetting the timer if there's current draw in DCP
if (current < CURRENT_THRESHOLD) { if (current < CURRENT_THRESHOLD) {
marker = tcnt; marker = uptime;
} }
break; break;
default: default:
@ -219,7 +221,7 @@ void white_usb_power_mode_tick(uint64_t tcnt){
break; break;
} }
#else #else
UNUSED(tcnt); UNUSED(uptime);
#endif #endif
} }
@ -236,7 +238,11 @@ bool white_check_ignition(void){
return !get_gpio_input(GPIOA, 1); return !get_gpio_input(GPIOA, 1);
} }
void white_init(void) { void white_set_phone_power(bool enabled){
UNUSED(enabled);
}
void white_grey_common_init(void) {
common_init_gpio(); common_init_gpio();
// C3: current sense // C3: current sense
@ -296,13 +302,6 @@ void white_init(void) {
// Set normal CAN mode // Set normal CAN mode
white_set_can_mode(CAN_MODE_NORMAL); white_set_can_mode(CAN_MODE_NORMAL);
// Setup ignition interrupts
SYSCFG->EXTICR[1] = SYSCFG_EXTICR1_EXTI1_PA;
EXTI->IMR |= (1U << 1);
EXTI->RTSR |= (1U << 1);
EXTI->FTSR |= (1U << 1);
NVIC_EnableIRQ(EXTI1_IRQn);
// Init usb power mode // Init usb power mode
uint32_t voltage = adc_get_voltage(); uint32_t voltage = adc_get_voltage();
// Init in CDP mode only if panda is powered by 12V. // Init in CDP mode only if panda is powered by 12V.
@ -314,6 +313,17 @@ void white_init(void) {
} }
} }
void white_init(void) {
white_grey_common_init();
// Set default state of ESP
#ifdef EON
current_board->set_esp_gps_mode(ESP_GPS_DISABLED);
#else
current_board->set_esp_gps_mode(ESP_GPS_ENABLED);
#endif
}
const harness_configuration white_harness_config = { const harness_configuration white_harness_config = {
.has_harness = false .has_harness = false
}; };
@ -332,5 +342,6 @@ const board board_white = {
.check_ignition = white_check_ignition, .check_ignition = white_check_ignition,
.read_current = white_read_current, .read_current = white_read_current,
.set_fan_power = white_set_fan_power, .set_fan_power = white_set_fan_power,
.set_ir_power = white_set_ir_power .set_ir_power = white_set_ir_power,
.set_phone_power = white_set_phone_power
}; };

@ -1,5 +1,8 @@
#define BOOTSTUB #define BOOTSTUB
#define VERS_TAG 0x53524556
#define MIN_VERSION 2
#include "config.h" #include "config.h"
#include "obj/gitversion.h" #include "obj/gitversion.h"
@ -29,7 +32,11 @@ const board *current_board;
// ********************* Includes ********************* // ********************* Includes *********************
#include "libc.h" #include "libc.h"
#include "provision.h" #include "provision.h"
#include "critical.h"
#include "faults.h"
#include "drivers/registers.h"
#include "drivers/interrupts.h"
#include "drivers/clock.h" #include "drivers/clock.h"
#include "drivers/llgpio.h" #include "drivers/llgpio.h"
#include "drivers/adc.h" #include "drivers/adc.h"
@ -65,6 +72,9 @@ extern void *_app_start[];
// BOUNTY: $200 coupon on shop.comma.ai or $100 check. // BOUNTY: $200 coupon on shop.comma.ai or $100 check.
int main(void) { int main(void) {
// Init interrupt table
init_interrupts(true);
disable_interrupts(); disable_interrupts();
clock_init(); clock_init();
detect_configuration(); detect_configuration();
@ -83,6 +93,13 @@ int main(void) {
uint8_t digest[SHA_DIGEST_SIZE]; uint8_t digest[SHA_DIGEST_SIZE];
SHA_hash(&_app_start[1], len-4, digest); SHA_hash(&_app_start[1], len-4, digest);
// verify version, last bytes in the signed area
uint32_t vers[2] = {0};
memcpy(&vers, ((void*)&_app_start[0]) + len - sizeof(vers), sizeof(vers));
if (vers[0] != VERS_TAG || vers[1] < MIN_VERSION) {
goto fail;
}
// verify RSA signature // verify RSA signature
if (RSA_verify(&release_rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) { if (RSA_verify(&release_rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) {
goto good; goto good;

@ -33,7 +33,7 @@ POSTCOMPILE = @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@
# this no longer pushes the bootstub # this no longer pushes the bootstub
flash: obj/$(PROJ_NAME).bin flash: obj/$(PROJ_NAME).bin
PYTHONPATH=../ python -c "from python import Panda; Panda().flash('obj/$(PROJ_NAME).bin')" PYTHONPATH=../ python3 -c "from python import Panda; Panda().flash('obj/$(PROJ_NAME).bin')"
ota: obj/$(PROJ_NAME).bin ota: obj/$(PROJ_NAME).bin
curl http://192.168.0.10/stupdate --upload-file $< curl http://192.168.0.10/stupdate --upload-file $<
@ -42,7 +42,7 @@ bin: obj/$(PROJ_NAME).bin
# this flashes everything # this flashes everything
recover: obj/bootstub.$(PROJ_NAME).bin obj/$(PROJ_NAME).bin recover: obj/bootstub.$(PROJ_NAME).bin obj/$(PROJ_NAME).bin
-PYTHONPATH=../ python -c "from python import Panda; Panda().reset(enter_bootloader=True)" -PYTHONPATH=../ python3 -c "from python import Panda; Panda().reset(enter_bootloader=True)"
sleep 1.0 sleep 1.0
$(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08004000 -D obj/$(PROJ_NAME).bin $(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08004000 -D obj/$(PROJ_NAME).bin
$(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08000000:leave -D obj/bootstub.$(PROJ_NAME).bin $(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08000000:leave -D obj/bootstub.$(PROJ_NAME).bin

@ -5,6 +5,7 @@
//#define DEBUG_UART //#define DEBUG_UART
//#define DEBUG_USB //#define DEBUG_USB
//#define DEBUG_SPI //#define DEBUG_SPI
//#define DEBUG_FAULTS
#ifdef STM32F4 #ifdef STM32F4
#define PANDA #define PANDA
@ -37,5 +38,8 @@
#define MAX_RESP_LEN 0x40U #define MAX_RESP_LEN 0x40U
// Around (1Mbps / 8 bits/byte / 12 bytes per message)
#define CAN_INTERRUPT_RATE 12000U
#endif #endif

@ -0,0 +1,23 @@
// ********************* Critical section helpers *********************
volatile bool interrupts_enabled = false;
void enable_interrupts(void) {
interrupts_enabled = true;
__enable_irq();
}
void disable_interrupts(void) {
interrupts_enabled = false;
__disable_irq();
}
uint8_t global_critical_depth = 0U;
#define ENTER_CRITICAL() \
__disable_irq(); \
global_critical_depth += 1U;
#define EXIT_CRITICAL() \
global_critical_depth -= 1U; \
if ((global_critical_depth == 0U) && interrupts_enabled) { \
__enable_irq(); \
}

@ -9,26 +9,16 @@
#define ADCCHAN_CURRENT 13 #define ADCCHAN_CURRENT 13
void adc_init(void) { void adc_init(void) {
// global setup register_set(&(ADC->CCR), ADC_CCR_TSVREFE | ADC_CCR_VBATE, 0xC30000U);
ADC->CCR = ADC_CCR_TSVREFE | ADC_CCR_VBATE; register_set(&(ADC1->CR2), ADC_CR2_ADON, 0xFF7F0F03U);
//ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EOCS | ADC_CR2_DDS; register_set(&(ADC1->SMPR1), ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13, 0x7FFFFFFU);
ADC1->CR2 = ADC_CR2_ADON;
// long
//ADC1->SMPR1 = ADC_SMPR1_SMP10 | ADC_SMPR1_SMP11 | ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13;
ADC1->SMPR1 = ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13;
} }
uint32_t adc_get(unsigned int channel) { uint32_t adc_get(unsigned int channel) {
// includes length // Select channel
//ADC1->SQR1 = 0; register_set(&(ADC1->JSQR), (channel << 15U), 0x3FFFFFU);
// select channel
ADC1->JSQR = channel << 15;
//ADC1->CR1 = ADC_CR1_DISCNUM_0;
//ADC1->CR1 = ADC_CR1_EOCIE;
// Start conversion
ADC1->SR &= ~(ADC_SR_JEOC); ADC1->SR &= ~(ADC_SR_JEOC);
ADC1->CR2 |= ADC_CR2_JSWSTART; ADC1->CR2 |= ADC_CR2_JSWSTART;
while (!(ADC1->SR & ADC_SR_JEOC)); while (!(ADC1->SR & ADC_SR_JEOC));

@ -27,11 +27,12 @@ void can_set_forwarding(int from, int to);
void can_init(uint8_t can_number); void can_init(uint8_t can_number);
void can_init_all(void); void can_init_all(void);
void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number); void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number, bool skip_tx_hook);
bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem); bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem);
// Ignition detected from CAN meessages // Ignition detected from CAN meessages
bool ignition_can = false; bool ignition_can = false;
uint32_t ignition_can_cnt = 0U;
// end API // end API
@ -148,23 +149,10 @@ void can_set_speed(uint8_t can_number) {
} }
} }
void can_init(uint8_t can_number) {
if (can_number != 0xffU) {
CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number);
can_set_speed(can_number);
llcan_init(CAN);
// in case there are queued up messages
process_can(can_number);
}
}
void can_init_all(void) { void can_init_all(void) {
for (uint8_t i=0U; i < CAN_MAX; i++) { for (uint8_t i=0U; i < CAN_MAX; i++) {
can_init(i); can_init(i);
} }
current_board->enable_can_transcievers(true);
} }
void can_flip_buses(uint8_t bus1, uint8_t bus2){ void can_flip_buses(uint8_t bus1, uint8_t bus2){
@ -336,11 +324,12 @@ void process_can(uint8_t can_number) {
} }
void ignition_can_hook(CAN_FIFOMailBox_TypeDef *to_push) { void ignition_can_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int bus = GET_BUS(to_push); int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push); int addr = GET_ADDR(to_push);
int len = GET_LEN(to_push); int len = GET_LEN(to_push);
ignition_can_cnt = 0U; // reset counter
if (bus == 0) { if (bus == 0) {
// GM exception // GM exception
if ((addr == 0x1F1) && (len == 8)) { if ((addr == 0x1F1) && (len == 8)) {
@ -357,11 +346,6 @@ void ignition_can_hook(CAN_FIFOMailBox_TypeDef *to_push) {
// this message isn't all zeros when ignition is on // this message isn't all zeros when ignition is on
ignition_can = GET_BYTES_04(to_push) != 0; ignition_can = GET_BYTES_04(to_push) != 0;
} }
// VW exception
if ((addr == 0x3C0) && (len == 4)) {
// VW Terminal 15 (ignition-on) state
ignition_can = (GET_BYTE(to_push, 2) & 0x2) != 0;
}
} }
} }
@ -394,7 +378,7 @@ void can_rx(uint8_t can_number) {
to_send.RDTR = to_push.RDTR; to_send.RDTR = to_push.RDTR;
to_send.RDLR = to_push.RDLR; to_send.RDLR = to_push.RDLR;
to_send.RDHR = to_push.RDHR; to_send.RDHR = to_push.RDHR;
can_send(&to_send, bus_fwd_num); can_send(&to_send, bus_fwd_num, true);
} }
safety_rx_hook(&to_push); safety_rx_hook(&to_push);
@ -408,20 +392,20 @@ void can_rx(uint8_t can_number) {
} }
} }
void CAN1_TX_IRQHandler(void) { process_can(0); } void CAN1_TX_IRQ_Handler(void) { process_can(0); }
void CAN1_RX0_IRQHandler(void) { can_rx(0); } void CAN1_RX0_IRQ_Handler(void) { can_rx(0); }
void CAN1_SCE_IRQHandler(void) { can_sce(CAN1); } void CAN1_SCE_IRQ_Handler(void) { can_sce(CAN1); }
void CAN2_TX_IRQHandler(void) { process_can(1); } void CAN2_TX_IRQ_Handler(void) { process_can(1); }
void CAN2_RX0_IRQHandler(void) { can_rx(1); } void CAN2_RX0_IRQ_Handler(void) { can_rx(1); }
void CAN2_SCE_IRQHandler(void) { can_sce(CAN2); } void CAN2_SCE_IRQ_Handler(void) { can_sce(CAN2); }
void CAN3_TX_IRQHandler(void) { process_can(2); } void CAN3_TX_IRQ_Handler(void) { process_can(2); }
void CAN3_RX0_IRQHandler(void) { can_rx(2); } void CAN3_RX0_IRQ_Handler(void) { can_rx(2); }
void CAN3_SCE_IRQHandler(void) { can_sce(CAN3); } void CAN3_SCE_IRQ_Handler(void) { can_sce(CAN3); }
void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number) { void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number, bool skip_tx_hook) {
if (safety_tx_hook(to_push) != 0) { if (skip_tx_hook || safety_tx_hook(to_push) != 0) {
if (bus_number < BUS_MAX) { if (bus_number < BUS_MAX) {
// add CAN packet to send queue // add CAN packet to send queue
// bus number isn't passed through // bus number isn't passed through
@ -440,3 +424,25 @@ void can_set_forwarding(int from, int to) {
can_forwarding[from] = to; can_forwarding[from] = to;
} }
void can_init(uint8_t can_number) {
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)
REGISTER_INTERRUPT(CAN2_TX_IRQn, CAN2_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
REGISTER_INTERRUPT(CAN2_RX0_IRQn, CAN2_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
REGISTER_INTERRUPT(CAN2_SCE_IRQn, CAN2_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
REGISTER_INTERRUPT(CAN3_TX_IRQn, CAN3_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
REGISTER_INTERRUPT(CAN3_RX0_IRQn, CAN3_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
REGISTER_INTERRUPT(CAN3_SCE_IRQn, CAN3_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
if (can_number != 0xffU) {
CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number);
can_set_speed(can_number);
llcan_init(CAN);
// in case there are queued up messages
process_can(can_number);
}
}

@ -1,25 +1,24 @@
void clock_init(void) { void clock_init(void) {
// enable external oscillator // enable external oscillator
RCC->CR |= RCC_CR_HSEON; register_set_bits(&(RCC->CR), RCC_CR_HSEON);
while ((RCC->CR & RCC_CR_HSERDY) == 0); while ((RCC->CR & RCC_CR_HSERDY) == 0);
// divide things // divide things
RCC->CFGR = RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_PPRE1_DIV4; register_set(&(RCC->CFGR), RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_PPRE1_DIV4, 0xFF7FFCF3U);
// 16mhz crystal // 16mhz crystal
RCC->PLLCFGR = RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLM_3 | register_set(&(RCC->PLLCFGR), RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLSRC_HSE, 0x7F437FFFU);
RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLSRC_HSE;
// start PLL // start PLL
RCC->CR |= RCC_CR_PLLON; register_set_bits(&(RCC->CR), RCC_CR_PLLON);
while ((RCC->CR & RCC_CR_PLLRDY) == 0); while ((RCC->CR & RCC_CR_PLLRDY) == 0);
// Configure Flash prefetch, Instruction cache, Data cache and wait state // Configure Flash prefetch, Instruction cache, Data cache and wait state
// *** without this, it breaks *** // *** without this, it breaks ***
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS; register_set(&(FLASH->ACR), FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS, 0x1F0FU);
// switch to PLL // switch to PLL
RCC->CFGR |= RCC_CFGR_SW_PLL; register_set_bits(&(RCC->CFGR), RCC_CFGR_SW_PLL);
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
// *** running on PLL *** // *** running on PLL ***
@ -27,14 +26,15 @@ void clock_init(void) {
void watchdog_init(void) { void watchdog_init(void) {
// setup watchdog // setup watchdog
IWDG->KR = 0x5555; IWDG->KR = 0x5555U;
IWDG->PR = 0; // divider /4 register_set(&(IWDG->PR), 0x0U, 0x7U); // divider/4
// 0 = 0.125 ms, let's have a 50ms watchdog // 0 = 0.125 ms, let's have a 50ms watchdog
IWDG->RLR = 400 - 1; register_set(&(IWDG->RLR), (400U-1U), 0xFFFU);
IWDG->KR = 0xCCCC; IWDG->KR = 0xCCCCU;
} }
void watchdog_feed(void) { void watchdog_feed(void) {
IWDG->KR = 0xAAAA; IWDG->KR = 0xAAAAU;
} }

@ -2,22 +2,19 @@ void puth(unsigned int i);
void puts(const char *a); void puts(const char *a);
void dac_init(void) { void dac_init(void) {
// no buffers required since we have an opamp // No buffers required since we have an opamp
//DAC->CR = DAC_CR_EN1 | DAC_CR_BOFF1 | DAC_CR_EN2 | DAC_CR_BOFF2; register_set(&(DAC->DHR12R1), 0U, 0xFFFU);
DAC->DHR12R1 = 0; register_set(&(DAC->DHR12R2), 0U, 0xFFFU);
DAC->DHR12R2 = 0; register_set(&(DAC->CR), DAC_CR_EN1 | DAC_CR_EN2, 0x3FFF3FFFU);
DAC->CR = DAC_CR_EN1 | DAC_CR_EN2;
} }
void dac_set(int channel, uint32_t value) { void dac_set(int channel, uint32_t value) {
if (channel == 0) { if (channel == 0) {
DAC->DHR12R1 = value; register_set(&(DAC->DHR12R1), value, 0xFFFU);
} else if (channel == 1) { } else if (channel == 1) {
DAC->DHR12R2 = value; register_set(&(DAC->DHR12R2), value, 0xFFFU);
} else { } else {
puts("Failed to set DAC: invalid channel value: "); puts("Failed to set DAC: invalid channel value: 0x"); puth(value); puts("\n");
puth(value);
puts("\n");
} }
} }

@ -1,15 +1,3 @@
void fan_init(void){
// Init PWM speed control
pwm_init(TIM3, 3);
// Init TACH interrupt
SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI2_PD;
EXTI->IMR |= (1U << 2);
EXTI->RTSR |= (1U << 2);
EXTI->FTSR |= (1U << 2);
NVIC_EnableIRQ(EXTI2_IRQn);
}
void fan_set_power(uint8_t percentage){ void fan_set_power(uint8_t percentage){
pwm_set(TIM3, 3, percentage); pwm_set(TIM3, 3, percentage);
} }
@ -27,10 +15,25 @@ void fan_tick(void){
} }
// TACH interrupt handler // TACH interrupt handler
void EXTI2_IRQHandler(void) { void EXTI2_IRQ_Handler(void) {
volatile unsigned int pr = EXTI->PR & (1U << 2); volatile unsigned int pr = EXTI->PR & (1U << 2);
if ((pr & (1U << 2)) != 0U) { if ((pr & (1U << 2)) != 0U) {
fan_tach_counter++; fan_tach_counter++;
} }
EXTI->PR = (1U << 2); EXTI->PR = (1U << 2);
}
void fan_init(void){
// 5000RPM * 4 tach edges / 60 seconds
REGISTER_INTERRUPT(EXTI2_IRQn, EXTI2_IRQ_Handler, 700U, FAULT_INTERRUPT_RATE_TACH)
// Init PWM speed control
pwm_init(TIM3, 3);
// Init TACH interrupt
register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI2_PD, 0xF00U);
register_set_bits(&(EXTI->IMR), (1U << 2));
register_set_bits(&(EXTI->RTSR), (1U << 2));
register_set_bits(&(EXTI->FTSR), (1U << 2));
NVIC_EnableIRQ(EXTI2_IRQn);
} }

@ -124,15 +124,15 @@ int get_bit_message(char *out, CAN_FIFOMailBox_TypeDef *to_bang) {
void setup_timer4(void) { void setup_timer4(void) {
// setup // setup
TIM4->PSC = 48-1; // tick on 1 us register_set(&(TIM4->PSC), (48-1), 0xFFFFU); // Tick on 1 us
TIM4->CR1 = TIM_CR1_CEN; // enable register_set(&(TIM4->CR1), TIM_CR1_CEN, 0x3FU); // Enable
TIM4->ARR = 30-1; // 33.3 kbps register_set(&(TIM4->ARR), (30-1), 0xFFFFU); // 33.3 kbps
// in case it's disabled // in case it's disabled
NVIC_EnableIRQ(TIM4_IRQn); NVIC_EnableIRQ(TIM4_IRQn);
// run the interrupt // run the interrupt
TIM4->DIER = TIM_DIER_UIE; // update interrupt register_set(&(TIM4->DIER), TIM_DIER_UIE, 0x5F5FU); // Update interrupt
TIM4->SR = 0; TIM4->SR = 0;
} }
@ -171,9 +171,9 @@ void reset_gmlan_switch_timeout(void) {
void set_bitbanged_gmlan(int val) { void set_bitbanged_gmlan(int val) {
if (val != 0) { if (val != 0) {
GPIOB->ODR |= (1U << 13); register_set_bits(&(GPIOB->ODR), (1U << 13));
} else { } else {
GPIOB->ODR &= ~(1U << 13); register_clear_bits(&(GPIOB->ODR), (1U << 13));
} }
} }
@ -187,7 +187,7 @@ int gmlan_fail_count = 0;
#define REQUIRED_SILENT_TIME 10 #define REQUIRED_SILENT_TIME 10
#define MAX_FAIL_COUNT 10 #define MAX_FAIL_COUNT 10
void TIM4_IRQHandler(void) { void TIM4_IRQ_Handler(void) {
if (gmlan_alt_mode == BITBANG) { if (gmlan_alt_mode == BITBANG) {
if ((TIM4->SR & TIM_SR_UIF) && (gmlan_sendmax != -1)) { if ((TIM4->SR & TIM_SR_UIF) && (gmlan_sendmax != -1)) {
int read = get_gpio_input(GPIOB, 12); int read = get_gpio_input(GPIOB, 12);
@ -231,8 +231,8 @@ void TIM4_IRQHandler(void) {
if ((gmlan_sending == gmlan_sendmax) || (gmlan_fail_count == MAX_FAIL_COUNT)) { if ((gmlan_sending == gmlan_sendmax) || (gmlan_fail_count == MAX_FAIL_COUNT)) {
set_bitbanged_gmlan(1); // recessive set_bitbanged_gmlan(1); // recessive
set_gpio_mode(GPIOB, 13, MODE_INPUT); set_gpio_mode(GPIOB, 13, MODE_INPUT);
TIM4->DIER = 0; // no update interrupt register_clear_bits(&(TIM4->DIER), TIM_DIER_UIE); // No update interrupt
TIM4->CR1 = 0; // disable timer register_set(&(TIM4->CR1), 0U, 0x3FU); // Disable timer
gmlan_sendmax = -1; // exit gmlan_sendmax = -1; // exit
} }
} }
@ -279,6 +279,8 @@ bool bitbang_gmlan(CAN_FIFOMailBox_TypeDef *to_bang) {
set_bitbanged_gmlan(1); // recessive set_bitbanged_gmlan(1); // recessive
set_gpio_mode(GPIOB, 13, MODE_OUTPUT); set_gpio_mode(GPIOB, 13, MODE_OUTPUT);
// 33kbps
REGISTER_INTERRUPT(TIM4_IRQn, TIM4_IRQ_Handler, 40000U, FAULT_INTERRUPT_RATE_GMLAN)
setup_timer4(); setup_timer4();
} }
return gmlan_send_ok; return gmlan_send_ok;

@ -8,7 +8,7 @@ uint8_t car_harness_status = 0U;
struct harness_configuration { struct harness_configuration {
const bool has_harness; const bool has_harness;
GPIO_TypeDef *GPIO_SBU1; GPIO_TypeDef *GPIO_SBU1;
GPIO_TypeDef *GPIO_SBU2; GPIO_TypeDef *GPIO_SBU2;
GPIO_TypeDef *GPIO_relay_normal; GPIO_TypeDef *GPIO_relay_normal;
GPIO_TypeDef *GPIO_relay_flipped; GPIO_TypeDef *GPIO_relay_flipped;
@ -52,28 +52,6 @@ bool harness_check_ignition(void) {
return ret; return ret;
} }
// TODO: refactor to use harness config
void harness_setup_ignition_interrupts(void){
if(car_harness_status == HARNESS_STATUS_NORMAL){
SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI3_PC;
EXTI->IMR |= (1U << 3);
EXTI->RTSR |= (1U << 3);
EXTI->FTSR |= (1U << 3);
puts("setup interrupts: normal\n");
} else if(car_harness_status == HARNESS_STATUS_FLIPPED) {
SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI0_PC;
EXTI->IMR |= (1U << 0);
EXTI->RTSR |= (1U << 0);
EXTI->FTSR |= (1U << 0);
NVIC_EnableIRQ(EXTI1_IRQn);
puts("setup interrupts: flipped\n");
} else {
puts("tried to setup ignition interrupts without harness connected\n");
}
NVIC_EnableIRQ(EXTI0_IRQn);
NVIC_EnableIRQ(EXTI3_IRQn);
}
uint8_t harness_detect_orientation(void) { uint8_t harness_detect_orientation(void) {
uint8_t ret = HARNESS_STATUS_NC; uint8_t ret = HARNESS_STATUS_NC;
@ -117,14 +95,11 @@ void harness_init(void) {
set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT); set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT);
} else { } else {
set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT); set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT);
} }
// keep busses connected by default // keep busses connected by default
set_intercept_relay(false); set_intercept_relay(false);
// setup ignition interrupts
harness_setup_ignition_interrupts();
} else { } else {
puts("failed to detect car harness!\n"); puts("failed to detect car harness!\n");
} }
} }

@ -0,0 +1,164 @@
typedef struct interrupt {
IRQn_Type irq_type;
void (*handler)(void);
uint32_t call_counter;
uint32_t max_call_rate; // Call rate is defined as the amount of calls each second
uint32_t call_rate_fault;
} interrupt;
void unused_interrupt_handler(void) {
// Something is wrong if this handler is called!
puts("Unused interrupt handler called!\n");
fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED);
}
#define NUM_INTERRUPTS 102U // There are 102 external interrupt sources (see stm32f413.h)
interrupt interrupts[NUM_INTERRUPTS];
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate, rate_fault) \
interrupts[irq_num].irq_type = irq_num; \
interrupts[irq_num].handler = func_ptr; \
interrupts[irq_num].call_counter = 0U; \
interrupts[irq_num].max_call_rate = call_rate; \
interrupts[irq_num].call_rate_fault = rate_fault;
bool check_interrupt_rate = false;
void handle_interrupt(IRQn_Type irq_type){
interrupts[irq_type].call_counter++;
interrupts[irq_type].handler();
// Check that the interrupts don't fire too often
if(check_interrupt_rate && (interrupts[irq_type].call_counter > interrupts[irq_type].max_call_rate)){
puts("Interrupt 0x"); puth(irq_type); puts(" fired too often (0x"); puth(interrupts[irq_type].call_counter); puts("/s)!\n");
fault_occurred(interrupts[irq_type].call_rate_fault);
}
}
// Reset interrupt counter every second
void TIM6_DAC_IRQ_Handler(void) {
if (TIM6->SR != 0) {
for(uint16_t i=0U; i<NUM_INTERRUPTS; i++){
interrupts[i].call_counter = 0U;
}
}
TIM6->SR = 0;
}
void init_interrupts(bool check_rate_limit){
check_interrupt_rate = check_rate_limit;
for(uint16_t i=0U; i<NUM_INTERRUPTS; i++){
interrupts[i].handler = unused_interrupt_handler;
}
// Init timer 10 for a 1s interval
register_set_bits(&(RCC->APB1ENR), RCC_APB1ENR_TIM6EN); // Enable interrupt timer peripheral
REGISTER_INTERRUPT(TIM6_DAC_IRQn, TIM6_DAC_IRQ_Handler, 1, FAULT_INTERRUPT_RATE_INTERRUPTS)
register_set(&(TIM6->PSC), (732-1), 0xFFFFU);
register_set(&(TIM6->DIER), TIM_DIER_UIE, 0x5F5FU);
register_set(&(TIM6->CR1), TIM_CR1_CEN, 0x3FU);
TIM6->SR = 0;
NVIC_EnableIRQ(TIM6_DAC_IRQn);
}
// ********************* Bare interrupt handlers *********************
// Only implemented the STM32F413 interrupts for now, the STM32F203 specific ones do not fall into the scope of SIL2
void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);}
void PVD_IRQHandler(void) {handle_interrupt(PVD_IRQn);}
void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);}
void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);}
void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);}
void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);}
void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);}
void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);}
void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);}
void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);}
void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);}
void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);}
void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);}
void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);}
void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);}
void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);}
void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);}
void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);}
void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);}
void CAN1_TX_IRQHandler(void) {handle_interrupt(CAN1_TX_IRQn);}
void CAN1_RX0_IRQHandler(void) {handle_interrupt(CAN1_RX0_IRQn);}
void CAN1_RX1_IRQHandler(void) {handle_interrupt(CAN1_RX1_IRQn);}
void CAN1_SCE_IRQHandler(void) {handle_interrupt(CAN1_SCE_IRQn);}
void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);}
void TIM1_BRK_TIM9_IRQHandler(void) {handle_interrupt(TIM1_BRK_TIM9_IRQn);}
void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);}
void TIM1_TRG_COM_TIM11_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_TIM11_IRQn);}
void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);}
void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);}
void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);}
void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);}
void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);}
void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);}
void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);}
void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);}
void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);}
void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);}
void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);}
void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);}
void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);}
void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);}
void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);}
void OTG_FS_WKUP_IRQHandler(void) {handle_interrupt(OTG_FS_WKUP_IRQn);}
void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);}
void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);}
void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);}
void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);}
void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);}
void FSMC_IRQHandler(void) {handle_interrupt(FSMC_IRQn);}
void SDIO_IRQHandler(void) {handle_interrupt(SDIO_IRQn);}
void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);}
void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);}
void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);}
void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);}
void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);}
void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);}
void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);}
void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);}
void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);}
void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);}
void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);}
void CAN2_TX_IRQHandler(void) {handle_interrupt(CAN2_TX_IRQn);}
void CAN2_RX0_IRQHandler(void) {handle_interrupt(CAN2_RX0_IRQn);}
void CAN2_RX1_IRQHandler(void) {handle_interrupt(CAN2_RX1_IRQn);}
void CAN2_SCE_IRQHandler(void) {handle_interrupt(CAN2_SCE_IRQn);}
void OTG_FS_IRQHandler(void) {handle_interrupt(OTG_FS_IRQn);}
void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);}
void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);}
void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);}
void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);}
void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);}
void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);}
#ifdef STM32F4
void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);}
void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);}
void CAN3_TX_IRQHandler(void) {handle_interrupt(CAN3_TX_IRQn);}
void CAN3_RX0_IRQHandler(void) {handle_interrupt(CAN3_RX0_IRQn);}
void CAN3_RX1_IRQHandler(void) {handle_interrupt(CAN3_RX1_IRQn);}
void CAN3_SCE_IRQHandler(void) {handle_interrupt(CAN3_SCE_IRQn);}
void RNG_IRQHandler(void) {handle_interrupt(RNG_IRQn);}
void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);}
void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);}
void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);}
void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);}
void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);}
void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);}
void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);}
void UART10_IRQHandler(void) {handle_interrupt(UART10_IRQn);}
void QUADSPI_IRQHandler(void) {handle_interrupt(QUADSPI_IRQn);}
void FMPI2C1_EV_IRQHandler(void) {handle_interrupt(FMPI2C1_EV_IRQn);}
void FMPI2C1_ER_IRQHandler(void) {handle_interrupt(FMPI2C1_ER_IRQn);}
void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);}
void DFSDM2_FLT0_IRQHandler(void) {handle_interrupt(DFSDM2_FLT0_IRQn);}
void DFSDM2_FLT1_IRQHandler(void) {handle_interrupt(DFSDM2_FLT1_IRQn);}
void DFSDM2_FLT2_IRQHandler(void) {handle_interrupt(DFSDM2_FLT2_IRQn);}
void DFSDM2_FLT3_IRQHandler(void) {handle_interrupt(DFSDM2_FLT3_IRQn);}
#endif

@ -19,25 +19,24 @@ void puts(const char *a);
bool llcan_set_speed(CAN_TypeDef *CAN_obj, uint32_t speed, bool loopback, bool silent) { bool llcan_set_speed(CAN_TypeDef *CAN_obj, uint32_t speed, bool loopback, bool silent) {
// initialization mode // initialization mode
CAN_obj->MCR = CAN_MCR_TTCM | CAN_MCR_INRQ; register_set(&(CAN_obj->MCR), CAN_MCR_TTCM | CAN_MCR_INRQ, 0x180FFU);
while((CAN_obj->MSR & CAN_MSR_INAK) != CAN_MSR_INAK); while((CAN_obj->MSR & CAN_MSR_INAK) != CAN_MSR_INAK);
// set time quanta from defines // set time quanta from defines
CAN_obj->BTR = (CAN_BTR_TS1_0 * (CAN_SEQ1-1)) | register_set(&(CAN_obj->BTR), ((CAN_BTR_TS1_0 * (CAN_SEQ1-1)) |
(CAN_BTR_TS2_0 * (CAN_SEQ2-1)) | (CAN_BTR_TS2_0 * (CAN_SEQ2-1)) |
(can_speed_to_prescaler(speed) - 1U); (can_speed_to_prescaler(speed) - 1U)), 0xC37F03FFU);
// silent loopback mode for debugging // silent loopback mode for debugging
if (loopback) { if (loopback) {
CAN_obj->BTR |= CAN_BTR_SILM | CAN_BTR_LBKM; register_set_bits(&(CAN_obj->BTR), CAN_BTR_SILM | CAN_BTR_LBKM);
} }
if (silent) { if (silent) {
CAN_obj->BTR |= CAN_BTR_SILM; register_set_bits(&(CAN_obj->BTR), CAN_BTR_SILM);
} }
// reset // reset
// cppcheck-suppress redundantAssignment ; it's a register register_set(&(CAN_obj->MCR), CAN_MCR_TTCM | CAN_MCR_ABOM, 0x180FFU);
CAN_obj->MCR = CAN_MCR_TTCM | CAN_MCR_ABOM;
#define CAN_TIMEOUT 1000000 #define CAN_TIMEOUT 1000000
int tmp = 0; int tmp = 0;
@ -51,20 +50,25 @@ bool llcan_set_speed(CAN_TypeDef *CAN_obj, uint32_t speed, bool loopback, bool s
} }
void llcan_init(CAN_TypeDef *CAN_obj) { void llcan_init(CAN_TypeDef *CAN_obj) {
// accept all filter // Enter init mode
CAN_obj->FMR |= CAN_FMR_FINIT; register_set_bits(&(CAN_obj->FMR), CAN_FMR_FINIT);
// Wait for INAK bit to be set
while(((CAN_obj->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)) {}
// no mask // no mask
CAN_obj->sFilterRegister[0].FR1 = 0; // For some weird reason some of these registers do not want to set properly on CAN2 and CAN3. Probably something to do with the single/dual mode and their different filters.
CAN_obj->sFilterRegister[0].FR2 = 0; CAN_obj->sFilterRegister[0].FR1 = 0U;
CAN_obj->sFilterRegister[14].FR1 = 0; CAN_obj->sFilterRegister[0].FR2 = 0U;
CAN_obj->sFilterRegister[14].FR2 = 0; CAN_obj->sFilterRegister[14].FR1 = 0U;
CAN_obj->sFilterRegister[14].FR2 = 0U;
CAN_obj->FA1R |= 1U | (1U << 14); CAN_obj->FA1R |= 1U | (1U << 14);
CAN_obj->FMR &= ~(CAN_FMR_FINIT); // Exit init mode, do not wait
register_clear_bits(&(CAN_obj->FMR), CAN_FMR_FINIT);
// enable certain CAN interrupts // enable certain CAN interrupts
CAN_obj->IER |= CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_WKUIE; register_set_bits(&(CAN_obj->IER), CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_WKUIE);
if (CAN_obj == CAN1) { if (CAN_obj == CAN1) {
NVIC_EnableIRQ(CAN1_TX_IRQn); NVIC_EnableIRQ(CAN1_TX_IRQn);
@ -87,7 +91,7 @@ void llcan_init(CAN_TypeDef *CAN_obj) {
void llcan_clear_send(CAN_TypeDef *CAN_obj) { void llcan_clear_send(CAN_TypeDef *CAN_obj) {
CAN_obj->TSR |= CAN_TSR_ABRQ0; CAN_obj->TSR |= CAN_TSR_ABRQ0;
CAN_obj->MSR &= ~(CAN_MSR_ERRI); register_clear_bits(&(CAN_obj->MSR), CAN_MSR_ERRI);
// cppcheck-suppress selfAssignment ; needed to clear the register // cppcheck-suppress selfAssignment ; needed to clear the register
CAN_obj->MSR = CAN_obj->MSR; CAN_obj->MSR = CAN_obj->MSR;
} }

@ -15,16 +15,16 @@ void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) {
uint32_t tmp = GPIO->MODER; uint32_t tmp = GPIO->MODER;
tmp &= ~(3U << (pin * 2U)); tmp &= ~(3U << (pin * 2U));
tmp |= (mode << (pin * 2U)); tmp |= (mode << (pin * 2U));
GPIO->MODER = tmp; register_set(&(GPIO->MODER), tmp, 0xFFFFFFFFU);
EXIT_CRITICAL(); EXIT_CRITICAL();
} }
void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) {
ENTER_CRITICAL(); ENTER_CRITICAL();
if (enabled) { if (enabled) {
GPIO->ODR |= (1U << pin); register_set_bits(&(GPIO->ODR), (1U << pin));
} else { } else {
GPIO->ODR &= ~(1U << pin); register_clear_bits(&(GPIO->ODR), (1U << pin));
} }
set_gpio_mode(GPIO, pin, MODE_OUTPUT); set_gpio_mode(GPIO, pin, MODE_OUTPUT);
EXIT_CRITICAL(); EXIT_CRITICAL();
@ -33,9 +33,9 @@ void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) {
void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){ void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){
ENTER_CRITICAL(); ENTER_CRITICAL();
if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { if(output_type == OUTPUT_TYPE_OPEN_DRAIN) {
GPIO->OTYPER |= (1U << pin); register_set_bits(&(GPIO->OTYPER), (1U << pin));
} else { } else {
GPIO->OTYPER &= ~(1U << pin); register_clear_bits(&(GPIO->OTYPER), (1U << pin));
} }
EXIT_CRITICAL(); EXIT_CRITICAL();
} }
@ -45,7 +45,7 @@ void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode)
uint32_t tmp = GPIO->AFR[pin >> 3U]; uint32_t tmp = GPIO->AFR[pin >> 3U];
tmp &= ~(0xFU << ((pin & 7U) * 4U)); tmp &= ~(0xFU << ((pin & 7U) * 4U));
tmp |= mode << ((pin & 7U) * 4U); tmp |= mode << ((pin & 7U) * 4U);
GPIO->AFR[pin >> 3] = tmp; register_set(&(GPIO->AFR[pin >> 3]), tmp, 0xFFFFFFFFU);
set_gpio_mode(GPIO, pin, MODE_ALTERNATE); set_gpio_mode(GPIO, pin, MODE_ALTERNATE);
EXIT_CRITICAL(); EXIT_CRITICAL();
} }
@ -55,7 +55,7 @@ void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) {
uint32_t tmp = GPIO->PUPDR; uint32_t tmp = GPIO->PUPDR;
tmp &= ~(3U << (pin * 2U)); tmp &= ~(3U << (pin * 2U));
tmp |= (mode << (pin * 2U)); tmp |= (mode << (pin * 2U));
GPIO->PUPDR = tmp; register_set(&(GPIO->PUPDR), tmp, 0xFFFFFFFFU);
EXIT_CRITICAL(); EXIT_CRITICAL();
} }

@ -1,53 +1,54 @@
#define PWM_COUNTER_OVERFLOW 2000U // To get ~50kHz #define PWM_COUNTER_OVERFLOW 2000U // To get ~50kHz
// TODO: Implement for 32-bit timers
void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ void pwm_init(TIM_TypeDef *TIM, uint8_t channel){
// Enable timer and auto-reload // Enable timer and auto-reload
TIM->CR1 = TIM_CR1_CEN | TIM_CR1_ARPE; register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU);
// Set channel as PWM mode 1 and enable output // Set channel as PWM mode 1 and enable output
switch(channel){ switch(channel){
case 1U: case 1U:
TIM->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE); register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE));
TIM->CCER |= TIM_CCER_CC1E; register_set_bits(&(TIM->CCER), TIM_CCER_CC1E);
break; break;
case 2U: case 2U:
TIM->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE); register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE));
TIM->CCER |= TIM_CCER_CC2E; register_set_bits(&(TIM->CCER), TIM_CCER_CC2E);
break; break;
case 3U: case 3U:
TIM->CCMR2 |= (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE); register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE));
TIM->CCER |= TIM_CCER_CC3E; register_set_bits(&(TIM->CCER), TIM_CCER_CC3E);
break; break;
case 4U: case 4U:
TIM->CCMR2 |= (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE); register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE));
TIM->CCER |= TIM_CCER_CC4E; register_set_bits(&(TIM->CCER), TIM_CCER_CC4E);
break; break;
default: default:
break; break;
} }
// Set max counter value // Set max counter value
TIM->ARR = PWM_COUNTER_OVERFLOW; register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU);
// Update registers and clear counter // Update registers and clear counter
TIM->EGR |= TIM_EGR_UG; TIM->EGR |= TIM_EGR_UG;
} }
// TODO: Implement for 32-bit timers
void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){
uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U);
switch(channel){ switch(channel){
case 1U: case 1U:
TIM->CCR1 = comp_value; register_set(&(TIM->CCR1), comp_value, 0xFFFFU);
break; break;
case 2U: case 2U:
TIM->CCR2 = comp_value; register_set(&(TIM->CCR2), comp_value, 0xFFFFU);
break; break;
case 3U: case 3U:
TIM->CCR3 = comp_value; register_set(&(TIM->CCR3), comp_value, 0xFFFFU);
break; break;
case 4U: case 4U:
TIM->CCR4 = comp_value; register_set(&(TIM->CCR4), comp_value, 0xFFFFU);
break; break;
default: default:
break; break;

@ -0,0 +1,81 @@
typedef struct reg {
volatile uint32_t *address;
uint32_t value;
uint32_t check_mask;
} reg;
// 10 bit hash with 23 as a prime
#define REGISTER_MAP_SIZE 0x3FFU
#define HASHING_PRIME 23U
#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != addr))
reg register_map[REGISTER_MAP_SIZE];
// Hash spread in first and second iterations seems to be reasonable.
// See: tests/development/register_hashmap_spread.py
// Also, check the collision warnings in the debug output, and minimize those.
uint16_t hash_addr(uint32_t input){
return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE);
}
// Do not put bits in the check mask that get changed by the hardware
void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask){
ENTER_CRITICAL()
// Set bits in register that are also in the mask
(*addr) = ((*addr) & (~mask)) | (val & mask);
// Add these values to the map
uint16_t hash = hash_addr((uint32_t) addr);
uint16_t tries = REGISTER_MAP_SIZE;
while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;}
if (tries != 0U){
register_map[hash].address = addr;
register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask);
register_map[hash].check_mask |= mask;
} else {
#ifdef DEBUG_FAULTS
puts("Hash collision: address 0x"); puth((uint32_t) addr); puts("!\n");
#endif
}
EXIT_CRITICAL()
}
// Set individual bits. Also add them to the check_mask.
// Do not use this to change bits that get reset by the hardware
void register_set_bits(volatile uint32_t *addr, uint32_t val) {
return register_set(addr, val, val);
}
// Clear individual bits. Also add them to the check_mask.
// Do not use this to clear bits that get set by the hardware
void register_clear_bits(volatile uint32_t *addr, uint32_t val) {
return register_set(addr, (~val), val);
}
// To be called periodically
void check_registers(void){
for(uint16_t i=0U; i<REGISTER_MAP_SIZE; i++){
if((uint32_t) register_map[i].address != 0U){
ENTER_CRITICAL()
if((*(register_map[i].address) & register_map[i].check_mask) != (register_map[i].value & register_map[i].check_mask)){
#ifdef DEBUG_FAULTS
puts("Register at address 0x"); puth((uint32_t) register_map[i].address); puts(" is divergent!");
puts(" Map: 0x"); puth(register_map[i].value);
puts(" Register: 0x"); puth(*(register_map[i].address));
puts(" Mask: 0x"); puth(register_map[i].check_mask);
puts("\n");
#endif
fault_occurred(FAULT_REGISTER_DIVERGENT);
}
EXIT_CRITICAL()
}
}
}
void init_registers(void) {
for(uint16_t i=0U; i<REGISTER_MAP_SIZE; i++){
register_map[i].address = (volatile uint32_t *) 0U;
register_map[i].check_mask = 0U;
}
}

@ -27,19 +27,19 @@ void rtc_init(void){
if((RCC->BDCR & RCC_BDCR_MASK) != RCC_BDCR_OPTIONS){ if((RCC->BDCR & RCC_BDCR_MASK) != RCC_BDCR_OPTIONS){
puts("Initializing RTC\n"); puts("Initializing RTC\n");
// Reset backup domain // Reset backup domain
RCC->BDCR |= RCC_BDCR_BDRST; register_set_bits(&(RCC->BDCR), RCC_BDCR_BDRST);
// Disable write protection // Disable write protection
PWR->CR |= PWR_CR_DBP; register_set_bits(&(PWR->CR), PWR_CR_DBP);
// Clear backup domain reset // Clear backup domain reset
RCC->BDCR &= ~(RCC_BDCR_BDRST); register_clear_bits(&(RCC->BDCR), RCC_BDCR_BDRST);
// Set RTC options // Set RTC options
RCC->BDCR = RCC_BDCR_OPTIONS | (RCC->BDCR & (~RCC_BDCR_MASK)); register_set(&(RCC->BDCR), RCC_BDCR_OPTIONS, RCC_BDCR_MASK);
// Enable write protection // Enable write protection
PWR->CR &= ~(PWR_CR_DBP); register_clear_bits(&(PWR->CR), PWR_CR_DBP);
} }
} }
} }
@ -49,12 +49,12 @@ void rtc_set_time(timestamp_t time){
puts("Setting RTC time\n"); puts("Setting RTC time\n");
// Disable write protection // Disable write protection
PWR->CR |= PWR_CR_DBP; register_set_bits(&(PWR->CR), PWR_CR_DBP);
RTC->WPR = 0xCA; RTC->WPR = 0xCA;
RTC->WPR = 0x53; RTC->WPR = 0x53;
// Enable initialization mode // Enable initialization mode
RTC->ISR |= RTC_ISR_INIT; register_set_bits(&(RTC->ISR), RTC_ISR_INIT);
while((RTC->ISR & RTC_ISR_INITF) == 0){} while((RTC->ISR & RTC_ISR_INITF) == 0){}
// Set time // Set time
@ -62,17 +62,17 @@ void rtc_set_time(timestamp_t time){
RTC->DR = (to_bcd(time.year - YEAR_OFFSET) << RTC_DR_YU_Pos) | (time.weekday << RTC_DR_WDU_Pos) | (to_bcd(time.month) << RTC_DR_MU_Pos) | (to_bcd(time.day) << RTC_DR_DU_Pos); RTC->DR = (to_bcd(time.year - YEAR_OFFSET) << RTC_DR_YU_Pos) | (time.weekday << RTC_DR_WDU_Pos) | (to_bcd(time.month) << RTC_DR_MU_Pos) | (to_bcd(time.day) << RTC_DR_DU_Pos);
// Set options // Set options
RTC->CR = 0U; register_set(&(RTC->CR), 0U, 0xFCFFFFU);
// Disable initalization mode // Disable initalization mode
RTC->ISR &= ~(RTC_ISR_INIT); register_clear_bits(&(RTC->ISR), RTC_ISR_INIT);
// Wait for synchronization // Wait for synchronization
while((RTC->ISR & RTC_ISR_RSF) == 0){} while((RTC->ISR & RTC_ISR_RSF) == 0){}
// Re-enable write protection // Re-enable write protection
RTC->WPR = 0x00; RTC->WPR = 0x00;
PWR->CR &= ~(PWR_CR_DBP); register_clear_bits(&(PWR->CR), PWR_CR_DBP);
} }
} }

@ -10,45 +10,22 @@ uint8_t spi_buf[SPI_BUF_SIZE];
int spi_buf_count = 0; int spi_buf_count = 0;
int spi_total_count = 0; int spi_total_count = 0;
void spi_init(void) {
//puts("SPI init\n");
SPI1->CR1 = SPI_CR1_SPE;
// enable SPI interrupts
//SPI1->CR2 = SPI_CR2_RXNEIE | SPI_CR2_ERRIE | SPI_CR2_TXEIE;
SPI1->CR2 = SPI_CR2_RXNEIE;
NVIC_EnableIRQ(DMA2_Stream2_IRQn);
NVIC_EnableIRQ(DMA2_Stream3_IRQn);
//NVIC_EnableIRQ(SPI1_IRQn);
// reset handshake back to pull up
set_gpio_mode(GPIOB, 0, MODE_INPUT);
set_gpio_pullup(GPIOB, 0, PULL_UP);
// setup interrupt on falling edge of SPI enable (on PA4)
SYSCFG->EXTICR[2] = SYSCFG_EXTICR2_EXTI4_PA;
EXTI->IMR |= (1U << 4);
EXTI->FTSR |= (1U << 4);
NVIC_EnableIRQ(EXTI4_IRQn);
}
void spi_tx_dma(void *addr, int len) { void spi_tx_dma(void *addr, int len) {
// disable DMA // disable DMA
SPI1->CR2 &= ~SPI_CR2_TXDMAEN; register_clear_bits(&(SPI1->CR2), SPI_CR2_TXDMAEN);
DMA2_Stream3->CR &= ~DMA_SxCR_EN; register_clear_bits(&(DMA2_Stream3->CR), DMA_SxCR_EN);
// DMA2, stream 3, channel 3 // DMA2, stream 3, channel 3
DMA2_Stream3->M0AR = (uint32_t)addr; register_set(&(DMA2_Stream3->M0AR), (uint32_t)addr, 0xFFFFFFFFU);
DMA2_Stream3->NDTR = len; DMA2_Stream3->NDTR = len;
DMA2_Stream3->PAR = (uint32_t)&(SPI1->DR); register_set(&(DMA2_Stream3->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU);
// channel3, increment memory, memory -> periph, enable // channel3, increment memory, memory -> periph, enable
DMA2_Stream3->CR = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_EN; register_set(&(DMA2_Stream3->CR), (DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_EN), 0x1E077EFEU);
delay(0); delay(0);
DMA2_Stream3->CR |= DMA_SxCR_TCIE; register_set_bits(&(DMA2_Stream3->CR), DMA_SxCR_TCIE);
SPI1->CR2 |= SPI_CR2_TXDMAEN; register_set_bits(&(SPI1->CR2), SPI_CR2_TXDMAEN);
// signal data is ready by driving low // signal data is ready by driving low
// esp must be configured as input by this point // esp must be configured as input by this point
@ -57,33 +34,32 @@ void spi_tx_dma(void *addr, int len) {
void spi_rx_dma(void *addr, int len) { void spi_rx_dma(void *addr, int len) {
// disable DMA // disable DMA
SPI1->CR2 &= ~SPI_CR2_RXDMAEN; register_clear_bits(&(SPI1->CR2), SPI_CR2_RXDMAEN);
DMA2_Stream2->CR &= ~DMA_SxCR_EN; register_clear_bits(&(DMA2_Stream2->CR), DMA_SxCR_EN);
// drain the bus // drain the bus
volatile uint8_t dat = SPI1->DR; volatile uint8_t dat = SPI1->DR;
(void)dat; (void)dat;
// DMA2, stream 2, channel 3 // DMA2, stream 2, channel 3
DMA2_Stream2->M0AR = (uint32_t)addr; register_set(&(DMA2_Stream2->M0AR), (uint32_t)addr, 0xFFFFFFFFU);
DMA2_Stream2->NDTR = len; DMA2_Stream2->NDTR = len;
DMA2_Stream2->PAR = (uint32_t)&(SPI1->DR); register_set(&(DMA2_Stream2->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU);
// channel3, increment memory, periph -> memory, enable // channel3, increment memory, periph -> memory, enable
DMA2_Stream2->CR = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_EN; register_set(&(DMA2_Stream2->CR), (DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_EN), 0x1E077EFEU);
delay(0); delay(0);
DMA2_Stream2->CR |= DMA_SxCR_TCIE; register_set_bits(&(DMA2_Stream2->CR), DMA_SxCR_TCIE);
SPI1->CR2 |= SPI_CR2_RXDMAEN; register_set_bits(&(SPI1->CR2), SPI_CR2_RXDMAEN);
} }
// ***************************** SPI IRQs ***************************** // ***************************** SPI IRQs *****************************
// can't go on the stack cause it's DMAed // can't go on the stack cause it's DMAed
uint8_t spi_tx_buf[0x44]; uint8_t spi_tx_buf[0x44];
// SPI RX // SPI RX
void DMA2_Stream2_IRQHandler(void) { void DMA2_Stream2_IRQ_Handler(void) {
int *resp_len = (int*)spi_tx_buf; int *resp_len = (int*)spi_tx_buf;
(void)memset(spi_tx_buf, 0xaa, 0x44); (void)memset(spi_tx_buf, 0xaa, 0x44);
*resp_len = spi_cb_rx(spi_buf, 0x14, spi_tx_buf+4); *resp_len = spi_cb_rx(spi_buf, 0x14, spi_tx_buf+4);
@ -99,7 +75,7 @@ void DMA2_Stream2_IRQHandler(void) {
} }
// SPI TX // SPI TX
void DMA2_Stream3_IRQHandler(void) { void DMA2_Stream3_IRQ_Handler(void) {
#ifdef DEBUG_SPI #ifdef DEBUG_SPI
puts("SPI handshake\n"); puts("SPI handshake\n");
#endif #endif
@ -112,7 +88,7 @@ void DMA2_Stream3_IRQHandler(void) {
DMA2->LIFCR = DMA_LIFCR_CTCIF3; DMA2->LIFCR = DMA_LIFCR_CTCIF3;
} }
void EXTI4_IRQHandler(void) { void EXTI4_IRQ_Handler(void) {
volatile unsigned int pr = EXTI->PR & (1U << 4); volatile unsigned int pr = EXTI->PR & (1U << 4);
#ifdef DEBUG_SPI #ifdef DEBUG_SPI
puts("exti4\n"); puts("exti4\n");
@ -125,3 +101,31 @@ void EXTI4_IRQHandler(void) {
EXTI->PR = pr; EXTI->PR = pr;
} }
// ***************************** SPI init *****************************
void spi_init(void) {
// Max SPI clock the ESP can produce is 80MHz. At buffer size of 256 bytes, that's a max of about 40k buffers per second
REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, 50000U, FAULT_INTERRUPT_RATE_SPI_DMA)
REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, 50000U, FAULT_INTERRUPT_RATE_SPI_DMA)
REGISTER_INTERRUPT(EXTI4_IRQn, EXTI4_IRQ_Handler, 50000U, FAULT_INTERRUPT_RATE_SPI_CS) // TODO: Figure out if this is a reasonable limit
//puts("SPI init\n");
register_set(&(SPI1->CR1), SPI_CR1_SPE, 0xFFFFU);
// enable SPI interrupts
//SPI1->CR2 = SPI_CR2_RXNEIE | SPI_CR2_ERRIE | SPI_CR2_TXEIE;
register_set(&(SPI1->CR2), SPI_CR2_RXNEIE, 0xF7U);
NVIC_EnableIRQ(DMA2_Stream2_IRQn);
NVIC_EnableIRQ(DMA2_Stream3_IRQn);
//NVIC_EnableIRQ(SPI1_IRQn);
// reset handshake back to pull up
set_gpio_mode(GPIOB, 0, MODE_INPUT);
set_gpio_pullup(GPIOB, 0, PULL_UP);
// setup interrupt on falling edge of SPI enable (on PA4)
register_set(&(SYSCFG->EXTICR[2]), SYSCFG_EXTICR2_EXTI4_PA, 0xFFFFU);
register_set_bits(&(EXTI->IMR), (1U << 4));
register_set_bits(&(EXTI->FTSR), (1U << 4));
NVIC_EnableIRQ(EXTI4_IRQn);
}

@ -1,7 +1,7 @@
void timer_init(TIM_TypeDef *TIM, int psc) { void timer_init(TIM_TypeDef *TIM, int psc) {
TIM->PSC = psc-1; register_set(&(TIM->PSC), (psc-1), 0xFFFFU);
TIM->DIER = TIM_DIER_UIE; register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU);
TIM->CR1 = TIM_CR1_CEN; register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU);
TIM->SR = 0; TIM->SR = 0;
} }

@ -197,12 +197,12 @@ void uart_interrupt_handler(uart_ring *q) {
EXIT_CRITICAL(); EXIT_CRITICAL();
} }
void USART1_IRQHandler(void) { uart_interrupt_handler(&uart_ring_esp_gps); } void USART1_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_esp_gps); }
void USART2_IRQHandler(void) { uart_interrupt_handler(&uart_ring_debug); } void USART2_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_debug); }
void USART3_IRQHandler(void) { uart_interrupt_handler(&uart_ring_lin2); } void USART3_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin2); }
void UART5_IRQHandler(void) { uart_interrupt_handler(&uart_ring_lin1); } void UART5_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin1); }
void DMA2_Stream5_IRQHandler(void) { void DMA2_Stream5_IRQ_Handler(void) {
ENTER_CRITICAL(); ENTER_CRITICAL();
// Handle errors // Handle errors
@ -272,6 +272,13 @@ void uart_set_baud(USART_TypeDef *u, unsigned int baud) {
} }
void uart_init(uart_ring *q, int baud) { void uart_init(uart_ring *q, int baud) {
// Register interrupts (max data rate: 115200 baud)
REGISTER_INTERRUPT(USART1_IRQn, USART1_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_1)
REGISTER_INTERRUPT(USART2_IRQn, USART2_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_2)
REGISTER_INTERRUPT(USART3_IRQn, USART3_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_3)
REGISTER_INTERRUPT(UART5_IRQn, UART5_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_5)
REGISTER_INTERRUPT(DMA2_Stream5_IRQn, DMA2_Stream5_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_UART_DMA) // Called twice per buffer
// Set baud and enable peripheral with TX and RX mode // Set baud and enable peripheral with TX and RX mode
uart_set_baud(q->uart, baud); uart_set_baud(q->uart, baud);
q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;

@ -281,15 +281,8 @@ uint8_t binary_object_store_desc[] = {
// BOS header // BOS header
BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header
BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType
0x40, 0x00, // wTotalLength (LSB, MSB) 0x39, 0x00, // wTotalLength (LSB, MSB)
0x03, // bNumDeviceCaps (USB 2.0 + WebUSB + WinUSB) 0x02, // bNumDeviceCaps (WebUSB + WinUSB)
// -------------------------------------------------
// USB 2.0 extension descriptor
0x07, // bLength, Descriptor size
0x10, // bDescriptorType, Device Capability Descriptor Type
0x02, // bDevCapabilityType, USB 2.0 extension capability type
0x00, 0x00, 0x00, 0x00, // bmAttributes, LIBUSB_BM_LPM_SUPPORT = 2 and its the only option
// ------------------------------------------------- // -------------------------------------------------
// WebUSB descriptor // WebUSB descriptor
@ -667,76 +660,7 @@ void usb_setup(void) {
} }
} }
void usb_init(void) {
// full speed PHY, do reset and remove power down
/*puth(USBx->GRSTCTL);
puts(" resetting PHY\n");*/
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0);
//puts("AHB idle\n");
// reset PHY here
USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST;
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST);
//puts("reset done\n");
// internal PHY, force device mode
USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_FDMOD;
// slowest timings
USBx->GUSBCFG |= ((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT);
// power up the PHY
#ifdef STM32F4
USBx->GCCFG = USB_OTG_GCCFG_PWRDWN;
//USBx->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_SDEN |USB_OTG_GCCFG_PDEN | USB_OTG_GCCFG_DCDEN;
/* B-peripheral session valid override enable*/
USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL;
USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN;
#else
USBx->GCCFG = USB_OTG_GCCFG_PWRDWN | USB_OTG_GCCFG_NOVBUSSENS;
#endif
// be a device, slowest timings
//USBx->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL;
//USBx->GUSBCFG |= (uint32_t)((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT);
//USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL;
// **** for debugging, doesn't seem to work ****
//USBx->GUSBCFG |= USB_OTG_GUSBCFG_CTXPKT;
// reset PHY clock
USBx_PCGCCTL = 0;
// enable the fancy OTG things
// DCFG_FRAME_INTERVAL_80 is 0
//USBx->GUSBCFG |= USB_OTG_GUSBCFG_HNPCAP | USB_OTG_GUSBCFG_SRPCAP;
USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK;
//USBx_DEVICE->DCFG = USB_OTG_DCFG_NZLSOHSK | USB_OTG_DCFG_DSPD;
//USBx_DEVICE->DCFG = USB_OTG_DCFG_DSPD;
// clear pending interrupts
USBx->GINTSTS = 0xBFFFFFFFU;
// setup USB interrupts
// all interrupts except TXFIFO EMPTY
//USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF);
//USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM);
USBx->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OTGINT |
USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_GONAKEFFM | USB_OTG_GINTMSK_GINAKEFFM |
USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_USBSUSPM |
USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM;
USBx->GAHBCFG = USB_OTG_GAHBCFG_GINT;
// DCTL startup value is 2 on new chip, 0 on old chip
USBx_DEVICE->DCTL = 0;
// enable the IRQ
NVIC_EnableIRQ(OTG_FS_IRQn);
}
// ***************************** USB port ***************************** // ***************************** USB port *****************************
@ -1008,7 +932,7 @@ void usb_irqhandler(void) {
//USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF);
} }
void OTG_FS_IRQHandler(void) { void OTG_FS_IRQ_Handler(void) {
NVIC_DisableIRQ(OTG_FS_IRQn); NVIC_DisableIRQ(OTG_FS_IRQn);
//__disable_irq(); //__disable_irq();
usb_irqhandler(); usb_irqhandler();
@ -1016,3 +940,77 @@ void OTG_FS_IRQHandler(void) {
NVIC_EnableIRQ(OTG_FS_IRQn); NVIC_EnableIRQ(OTG_FS_IRQn);
} }
// ***************************** USB init *****************************
void usb_init(void) {
REGISTER_INTERRUPT(OTG_FS_IRQn, OTG_FS_IRQ_Handler, 1500000U, FAULT_INTERRUPT_RATE_USB) //TODO: Find out a better rate limit for USB. Now it's the 1.5MB/s rate
// full speed PHY, do reset and remove power down
/*puth(USBx->GRSTCTL);
puts(" resetting PHY\n");*/
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0);
//puts("AHB idle\n");
// reset PHY here
USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST;
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST);
//puts("reset done\n");
// internal PHY, force device mode
USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_FDMOD;
// slowest timings
USBx->GUSBCFG |= ((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT);
// power up the PHY
#ifdef STM32F4
USBx->GCCFG = USB_OTG_GCCFG_PWRDWN;
//USBx->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_SDEN |USB_OTG_GCCFG_PDEN | USB_OTG_GCCFG_DCDEN;
/* B-peripheral session valid override enable*/
USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL;
USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN;
#else
USBx->GCCFG = USB_OTG_GCCFG_PWRDWN | USB_OTG_GCCFG_NOVBUSSENS;
#endif
// be a device, slowest timings
//USBx->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL;
//USBx->GUSBCFG |= (uint32_t)((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT);
//USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL;
// **** for debugging, doesn't seem to work ****
//USBx->GUSBCFG |= USB_OTG_GUSBCFG_CTXPKT;
// reset PHY clock
USBx_PCGCCTL = 0;
// enable the fancy OTG things
// DCFG_FRAME_INTERVAL_80 is 0
//USBx->GUSBCFG |= USB_OTG_GUSBCFG_HNPCAP | USB_OTG_GUSBCFG_SRPCAP;
USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK;
//USBx_DEVICE->DCFG = USB_OTG_DCFG_NZLSOHSK | USB_OTG_DCFG_DSPD;
//USBx_DEVICE->DCFG = USB_OTG_DCFG_DSPD;
// clear pending interrupts
USBx->GINTSTS = 0xBFFFFFFFU;
// setup USB interrupts
// all interrupts except TXFIFO EMPTY
//USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF);
//USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM);
USBx->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OTGINT |
USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_GONAKEFFM | USB_OTG_GINTMSK_GINAKEFFM |
USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_USBSUSPM |
USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM;
USBx->GAHBCFG = USB_OTG_GAHBCFG_GINT;
// DCTL startup value is 2 on new chip, 0 on old chip
USBx_DEVICE->DCTL = 0;
// enable the IRQ
NVIC_EnableIRQ(OTG_FS_IRQn);
}

@ -0,0 +1,49 @@
#define FAULT_STATUS_NONE 0U
#define FAULT_STATUS_TEMPORARY 1U
#define FAULT_STATUS_PERMANENT 2U
// Fault types
#define FAULT_RELAY_MALFUNCTION (1U << 0)
#define FAULT_UNUSED_INTERRUPT_HANDLED (1U << 1)
#define FAULT_INTERRUPT_RATE_CAN_1 (1U << 2)
#define FAULT_INTERRUPT_RATE_CAN_2 (1U << 3)
#define FAULT_INTERRUPT_RATE_CAN_3 (1U << 4)
#define FAULT_INTERRUPT_RATE_TACH (1U << 5)
#define FAULT_INTERRUPT_RATE_GMLAN (1U << 6)
#define FAULT_INTERRUPT_RATE_INTERRUPTS (1U << 7)
#define FAULT_INTERRUPT_RATE_SPI_DMA (1U << 8)
#define FAULT_INTERRUPT_RATE_SPI_CS (1U << 9)
#define FAULT_INTERRUPT_RATE_UART_1 (1U << 10)
#define FAULT_INTERRUPT_RATE_UART_2 (1U << 11)
#define FAULT_INTERRUPT_RATE_UART_3 (1U << 12)
#define FAULT_INTERRUPT_RATE_UART_5 (1U << 13)
#define FAULT_INTERRUPT_RATE_UART_DMA (1U << 14)
#define FAULT_INTERRUPT_RATE_USB (1U << 15)
#define FAULT_INTERRUPT_RATE_TIM1 (1U << 16)
#define FAULT_INTERRUPT_RATE_TIM3 (1U << 17)
#define FAULT_REGISTER_DIVERGENT (1U << 18)
// Permanent faults
#define PERMANENT_FAULTS 0U
uint8_t fault_status = FAULT_STATUS_NONE;
uint32_t faults = 0U;
void fault_occurred(uint32_t fault) {
faults |= fault;
if((PERMANENT_FAULTS & fault) != 0U){
puts("Permanent fault occurred: 0x"); puth(fault); puts("\n");
fault_status = FAULT_STATUS_PERMANENT;
} else {
puts("Temporary fault occurred: 0x"); puth(fault); puts("\n");
fault_status = FAULT_STATUS_TEMPORARY;
}
}
void fault_recovered(uint32_t fault) {
if((PERMANENT_FAULTS & fault) == 0U){
faults &= ~fault;
} else {
puts("Cannot recover from a permanent fault!\n");
}
}

@ -23,6 +23,9 @@ void early(void) {
// Reset global critical depth // Reset global critical depth
global_critical_depth = 0; global_critical_depth = 0;
// Init register and interrupt tables
init_registers();
// neccesary for DFU flashing on a non-power cycled white panda // neccesary for DFU flashing on a non-power cycled white panda
enable_interrupts(); enable_interrupts();
@ -59,13 +62,6 @@ void early(void) {
detect_configuration(); detect_configuration();
detect_board_type(); detect_board_type();
#ifdef PANDA
// enable the ESP, disable ESP boot mode
// dont disable on grey panda
current_board->set_esp_gps_mode(ESP_GPS_ENABLED);
#endif
if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) { if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) {
#ifdef PANDA #ifdef PANDA
current_board->set_esp_gps_mode(ESP_GPS_DISABLED); current_board->set_esp_gps_mode(ESP_GPS_DISABLED);

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d0d192a64b7d859a76a631cf9bfdb865825ca83172a455eff661217c56392638 oid sha256:7c2cb1deecee360e4e8069c4215f05f652e14c70bd88f9bd072dc5502a5fd530
size 1145211 size 1145325

@ -40,28 +40,3 @@ int memcmp(const void * ptr1, const void * ptr2, unsigned int num) {
return ret; return ret;
} }
// ********************* IRQ helpers *********************
volatile bool interrupts_enabled = false;
void enable_interrupts(void) {
interrupts_enabled = true;
__enable_irq();
}
void disable_interrupts(void) {
interrupts_enabled = false;
__disable_irq();
}
uint8_t global_critical_depth = 0U;
#define ENTER_CRITICAL() \
__disable_irq(); \
global_critical_depth += 1U;
#define EXIT_CRITICAL() \
global_critical_depth -= 1U; \
if ((global_critical_depth == 0U) && interrupts_enabled) { \
__enable_irq(); \
}

@ -5,10 +5,15 @@
#include "config.h" #include "config.h"
#include "obj/gitversion.h" #include "obj/gitversion.h"
#include "main_declarations.h"
#include "critical.h"
#include "libc.h" #include "libc.h"
#include "provision.h" #include "provision.h"
#include "faults.h"
#include "main_declarations.h" #include "drivers/registers.h"
#include "drivers/interrupts.h"
#include "drivers/llcan.h" #include "drivers/llcan.h"
#include "drivers/llgpio.h" #include "drivers/llgpio.h"
@ -34,6 +39,28 @@
#include "drivers/can.h" #include "drivers/can.h"
extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used
struct __attribute__((packed)) health_t {
uint32_t uptime_pkt;
uint32_t voltage_pkt;
uint32_t current_pkt;
uint32_t can_send_errs_pkt;
uint32_t can_fwd_errs_pkt;
uint32_t gmlan_send_errs_pkt;
uint32_t faults_pkt;
uint8_t ignition_line_pkt;
uint8_t ignition_can_pkt;
uint8_t controls_allowed_pkt;
uint8_t gas_interceptor_detected_pkt;
uint8_t car_harness_status_pkt;
uint8_t usb_power_mode_pkt;
uint8_t safety_mode_pkt;
uint8_t fault_status_pkt;
uint8_t power_save_enabled_pkt;
};
// ********************* Serial debugging ********************* // ********************* Serial debugging *********************
bool check_started(void) { bool check_started(void) {
@ -45,11 +72,14 @@ void debug_ring_callback(uart_ring *ring) {
while (getc(ring, &rcv)) { while (getc(ring, &rcv)) {
(void)putc(ring, rcv); // misra-c2012-17.7: cast to void is ok: debug function (void)putc(ring, rcv); // misra-c2012-17.7: cast to void is ok: debug function
// jump to DFU flash // only allow bootloader entry on debug builds
if (rcv == 'z') { #ifdef ALLOW_DEBUG
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; // jump to DFU flash
NVIC_SystemReset(); if (rcv == 'z') {
} enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
NVIC_SystemReset();
}
#endif
// normal reset // normal reset
if (rcv == 'x') { if (rcv == 'x') {
@ -72,100 +102,65 @@ void debug_ring_callback(uart_ring *ring) {
} }
} }
// ***************************** started logic *****************************
void started_interrupt_handler(uint8_t interrupt_line) {
volatile unsigned int pr = EXTI->PR & (1U << interrupt_line);
if ((pr & (1U << interrupt_line)) != 0U) {
#ifdef DEBUG
puts("got started interrupt\n");
#endif
// jenky debounce
delay(100000);
#ifdef EON
// set power savings mode here if on EON build
int power_save_state = check_started() ? POWER_SAVE_STATUS_DISABLED : POWER_SAVE_STATUS_ENABLED;
set_power_save_state(power_save_state);
// set CDP usb power mode everytime that the car starts to make sure EON is charging
if (check_started()) {
current_board->set_usb_power_mode(USB_POWER_CDP);
}
#endif
}
EXTI->PR = (1U << interrupt_line);
}
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck
void EXTI0_IRQHandler(void) {
started_interrupt_handler(0);
}
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck
void EXTI1_IRQHandler(void) {
started_interrupt_handler(1);
}
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck
void EXTI3_IRQHandler(void) {
started_interrupt_handler(3);
}
// ****************************** safety mode ****************************** // ****************************** safety mode ******************************
// this is the only way to leave silent mode // this is the only way to leave silent mode
void set_safety_mode(uint16_t mode, int16_t param) { void set_safety_mode(uint16_t mode, int16_t param) {
int err = safety_set_mode(mode, param); uint16_t mode_copy = mode;
int err = set_safety_hooks(mode_copy, param);
if (err == -1) { if (err == -1) {
puts("Error: safety set mode failed\n"); puts("Error: safety set mode failed. Falling back to SILENT\n");
} else { mode_copy = SAFETY_SILENT;
switch (mode) { err = set_safety_hooks(mode_copy, 0);
case SAFETY_NOOUTPUT: if (err == -1) {
set_intercept_relay(false); puts("Error: Failed setting SILENT mode. Hanging\n");
if(board_has_obd()){ while (true) {
current_board->set_can_mode(CAN_MODE_NORMAL); // TERMINAL ERROR: we can't continue if SILENT safety mode isn't succesfully set
} }
can_silent = ALL_CAN_SILENT; }
break; }
case SAFETY_ELM327: switch (mode_copy) {
set_intercept_relay(false); case SAFETY_SILENT:
heartbeat_counter = 0U; set_intercept_relay(false);
if(board_has_obd()){ if (board_has_obd()) {
current_board->set_can_mode(CAN_MODE_OBD_CAN2); current_board->set_can_mode(CAN_MODE_NORMAL);
}
can_silent = ALL_CAN_LIVE;
break;
default:
set_intercept_relay(true);
heartbeat_counter = 0U;
if(board_has_obd()){
current_board->set_can_mode(CAN_MODE_NORMAL);
}
can_silent = ALL_CAN_LIVE;
break;
} }
can_init_all(); can_silent = ALL_CAN_SILENT;
break;
case SAFETY_NOOUTPUT:
set_intercept_relay(false);
if (board_has_obd()) {
current_board->set_can_mode(CAN_MODE_NORMAL);
}
can_silent = ALL_CAN_LIVE;
break;
case SAFETY_ELM327:
set_intercept_relay(false);
heartbeat_counter = 0U;
if (board_has_obd()) {
current_board->set_can_mode(CAN_MODE_OBD_CAN2);
}
can_silent = ALL_CAN_LIVE;
break;
default:
set_intercept_relay(true);
heartbeat_counter = 0U;
if (board_has_obd()) {
current_board->set_can_mode(CAN_MODE_NORMAL);
}
can_silent = ALL_CAN_LIVE;
break;
} }
can_init_all();
} }
// ***************************** USB port ***************************** // ***************************** USB port *****************************
int get_health_pkt(void *dat) { int get_health_pkt(void *dat) {
struct __attribute__((packed)) { COMPILE_TIME_ASSERT(sizeof(struct health_t) <= MAX_RESP_LEN);
uint32_t voltage_pkt; struct health_t * health = (struct health_t*)dat;
uint32_t current_pkt;
uint32_t can_send_errs_pkt;
uint32_t can_fwd_errs_pkt;
uint32_t gmlan_send_errs_pkt;
uint8_t ignition_line_pkt;
uint8_t ignition_can_pkt;
uint8_t controls_allowed_pkt;
uint8_t gas_interceptor_detected_pkt;
uint8_t car_harness_status_pkt;
uint8_t usb_power_mode_pkt;
uint8_t safety_mode_pkt;
} *health = dat;
health->uptime_pkt = uptime_cnt;
health->voltage_pkt = adc_get_voltage(); health->voltage_pkt = adc_get_voltage();
health->current_pkt = current_board->read_current(); health->current_pkt = current_board->read_current();
@ -181,6 +176,10 @@ int get_health_pkt(void *dat) {
health->car_harness_status_pkt = car_harness_status; health->car_harness_status_pkt = car_harness_status;
health->usb_power_mode_pkt = usb_power_mode; health->usb_power_mode_pkt = usb_power_mode;
health->safety_mode_pkt = (uint8_t)(current_safety_mode); health->safety_mode_pkt = (uint8_t)(current_safety_mode);
health->power_save_enabled_pkt = (uint8_t)(power_save_status == POWER_SAVE_STATUS_ENABLED);
health->fault_status_pkt = fault_status;
health->faults_pkt = faults;
return sizeof(*health); return sizeof(*health);
} }
@ -230,7 +229,7 @@ void usb_cb_ep3_out(void *usbdata, int len, bool hardwired) {
to_push.RIR = d32[dpkt]; to_push.RIR = d32[dpkt];
uint8_t bus_number = (to_push.RDTR >> 4) & CAN_BUS_NUM_MASK; uint8_t bus_number = (to_push.RDTR >> 4) & CAN_BUS_NUM_MASK;
can_send(&to_push, bus_number); can_send(&to_push, bus_number, false);
} }
} }
@ -313,6 +312,10 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired)
resp[1] = ((fan_rpm & 0xFF00U) >> 8U); resp[1] = ((fan_rpm & 0xFF00U) >> 8U);
resp_len = 2; resp_len = 2;
break; break;
// **** 0xb3: set phone power
case 0xb3:
current_board->set_phone_power(setup->b.wValue.w > 0U);
break;
// **** 0xc0: get CAN debug info // **** 0xc0: get CAN debug info
case 0xc0: case 0xc0:
puts("can tx: "); puth(can_tx_cnt); puts("can tx: "); puth(can_tx_cnt);
@ -366,6 +369,24 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired)
case 0xd2: case 0xd2:
resp_len = get_health_pkt(resp); resp_len = get_health_pkt(resp);
break; break;
// **** 0xd3: get first 64 bytes of signature
case 0xd3:
{
resp_len = 64;
char * code = (char*)_app_start;
int code_len = _app_start[0];
(void)memcpy(resp, &code[code_len], resp_len);
}
break;
// **** 0xd4: get second 64 bytes of signature
case 0xd4:
{
resp_len = 64;
char * code = (char*)_app_start;
int code_len = _app_start[0];
(void)memcpy(resp, &code[code_len + 64], resp_len);
}
break;
// **** 0xd6: get version // **** 0xd6: get version
case 0xd6: case 0xd6:
COMPILE_TIME_ASSERT(sizeof(gitversion) <= MAX_RESP_LEN); COMPILE_TIME_ASSERT(sizeof(gitversion) <= MAX_RESP_LEN);
@ -427,8 +448,10 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired)
// **** 0xdc: set safety mode // **** 0xdc: set safety mode
case 0xdc: case 0xdc:
// Blocked over WiFi. // Blocked over WiFi.
// Allow NOOUTPUT and ELM security mode to be set over wifi. // Allow SILENT, NOOUTPUT and ELM security mode to be set over wifi.
if (hardwired || (setup->b.wValue.w == SAFETY_NOOUTPUT) || (setup->b.wValue.w == SAFETY_ELM327)) { if (hardwired || (setup->b.wValue.w == SAFETY_SILENT) ||
(setup->b.wValue.w == SAFETY_NOOUTPUT) ||
(setup->b.wValue.w == SAFETY_ELM327)) {
set_safety_mode(setup->b.wValue.w, (uint16_t) setup->b.wIndex.w); set_safety_mode(setup->b.wValue.w, (uint16_t) setup->b.wIndex.w);
} }
break; break;
@ -526,6 +549,10 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired)
case 0xe6: case 0xe6:
current_board->set_usb_power_mode(setup->b.wValue.w); current_board->set_usb_power_mode(setup->b.wValue.w);
break; break;
// **** 0xe7: set power save state
case 0xe7:
set_power_save_state(setup->b.wValue.w);
break;
// **** 0xf0: do k-line wValue pulse on uart2 for Acura // **** 0xf0: do k-line wValue pulse on uart2 for Acura
case 0xf0: case 0xf0:
if (setup->b.wValue.w == 1U) { if (setup->b.wValue.w == 1U) {
@ -640,24 +667,21 @@ void __attribute__ ((noinline)) enable_fpu(void) {
SCB->CPACR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U))); SCB->CPACR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U)));
} }
uint64_t tcnt = 0; // go into SILENT when the EON does not send a heartbeat for this amount of seconds.
// go into NOOUTPUT when the EON does not send a heartbeat for this amount of seconds.
#define EON_HEARTBEAT_IGNITION_CNT_ON 5U #define EON_HEARTBEAT_IGNITION_CNT_ON 5U
#define EON_HEARTBEAT_IGNITION_CNT_OFF 2U #define EON_HEARTBEAT_IGNITION_CNT_OFF 2U
// called once per second // called once per second
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void TIM1_BRK_TIM9_IRQ_Handler(void) {
void TIM1_BRK_TIM9_IRQHandler(void) {
if (TIM9->SR != 0) { if (TIM9->SR != 0) {
can_live = pending_can_live; can_live = pending_can_live;
current_board->usb_power_mode_tick(tcnt); current_board->usb_power_mode_tick(uptime_cnt);
//puth(usart1_dma); puts(" "); puth(DMA2_Stream5->M0AR); puts(" "); puth(DMA2_Stream5->NDTR); puts("\n"); //puth(usart1_dma); puts(" "); puth(DMA2_Stream5->M0AR); puts(" "); puth(DMA2_Stream5->NDTR); puts("\n");
// reset this every 16th pass // reset this every 16th pass
if ((tcnt & 0xFU) == 0U) { if ((uptime_cnt & 0xFU) == 0U) {
pending_can_live = 0; pending_can_live = 0;
} }
#ifdef DEBUG #ifdef DEBUG
@ -676,30 +700,57 @@ void TIM1_BRK_TIM9_IRQHandler(void) {
// turn off the blue LED, turned on by CAN // turn off the blue LED, turned on by CAN
// unless we are in power saving mode // unless we are in power saving mode
current_board->set_led(LED_BLUE, (tcnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED)); current_board->set_led(LED_BLUE, (uptime_cnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED));
// increase heartbeat counter and cap it at the uint32 limit // increase heartbeat counter and cap it at the uint32 limit
if (heartbeat_counter < __UINT32_MAX__) { if (heartbeat_counter < __UINT32_MAX__) {
heartbeat_counter += 1U; heartbeat_counter += 1U;
} }
// check heartbeat counter if we are running EON code. If the heartbeat has been gone for a while, go to NOOUTPUT safety mode.
#ifdef EON #ifdef EON
// check heartbeat counter if we are running EON code.
// if the heartbeat has been gone for a while, go to SILENT safety mode and enter power save
if (heartbeat_counter >= (check_started() ? EON_HEARTBEAT_IGNITION_CNT_ON : EON_HEARTBEAT_IGNITION_CNT_OFF)) { if (heartbeat_counter >= (check_started() ? EON_HEARTBEAT_IGNITION_CNT_ON : EON_HEARTBEAT_IGNITION_CNT_OFF)) {
puts("EON hasn't sent a heartbeat for 0x"); puth(heartbeat_counter); puts(" seconds. Safety is set to NOOUTPUT mode.\n"); puts("EON hasn't sent a heartbeat for 0x");
if(current_safety_mode != SAFETY_NOOUTPUT){ puth(heartbeat_counter);
set_safety_mode(SAFETY_NOOUTPUT, 0U); puts(" seconds. Safety is set to SILENT mode.\n");
if (current_safety_mode != SAFETY_SILENT) {
set_safety_mode(SAFETY_SILENT, 0U);
}
if (power_save_status != POWER_SAVE_STATUS_ENABLED) {
set_power_save_state(POWER_SAVE_STATUS_ENABLED);
} }
} }
// enter CDP mode when car starts to ensure we are charging a turned off EON
if (check_started() && (usb_power_mode != USB_POWER_CDP)) {
current_board->set_usb_power_mode(USB_POWER_CDP);
}
#endif #endif
// check registers
check_registers();
// set ignition_can to false after 2s of no CAN seen
if (ignition_can_cnt > 2U) {
ignition_can = false;
};
// on to the next one // on to the next one
tcnt += 1U; uptime_cnt += 1U;
safety_mode_cnt += 1U;
ignition_can_cnt += 1U;
} }
TIM9->SR = 0; TIM9->SR = 0;
} }
int main(void) { int main(void) {
// Init interrupt table
init_interrupts(true);
// 1s timer
REGISTER_INTERRUPT(TIM1_BRK_TIM9_IRQn, TIM1_BRK_TIM9_IRQ_Handler, 2U, FAULT_INTERRUPT_RATE_TIM1)
// shouldn't have interrupts here, but just in case // shouldn't have interrupts here, but just in case
disable_interrupts(); disable_interrupts();
@ -760,32 +811,16 @@ int main(void) {
TIM2->EGR = TIM_EGR_UG; TIM2->EGR = TIM_EGR_UG;
// use TIM2->CNT to read // use TIM2->CNT to read
// default to silent mode to prevent issues with Ford // init to SILENT and can silent
// hardcode a specific safety mode if you want to force the panda to be in a specific mode set_safety_mode(SAFETY_SILENT, 0);
int err = safety_set_mode(SAFETY_NOOUTPUT, 0);
if (err == -1) { // enable CAN TXs
puts("Failed to set safety mode\n"); current_board->enable_can_transcievers(true);
while (true) {
// if SAFETY_NOOUTPUT isn't succesfully set, we can't continue
}
}
can_silent = ALL_CAN_SILENT;
can_init_all();
#ifndef EON #ifndef EON
spi_init(); spi_init();
#endif #endif
#ifdef EON
// have to save power
if (hw_type == HW_TYPE_WHITE_PANDA) {
current_board->set_esp_gps_mode(ESP_GPS_DISABLED);
}
// only enter power save after the first cycle
/*if (check_started()) {
set_power_save_state(POWER_SAVE_STATUS_ENABLED);
}*/
#endif
// 1hz // 1hz
timer_init(TIM9, 1464); timer_init(TIM9, 1464);
NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn); NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
@ -804,19 +839,30 @@ int main(void) {
for (cnt=0;;cnt++) { for (cnt=0;;cnt++) {
if (power_save_status == POWER_SAVE_STATUS_DISABLED) { if (power_save_status == POWER_SAVE_STATUS_DISABLED) {
int div_mode = ((usb_power_mode == USB_POWER_DCP) ? 4 : 1); #ifdef DEBUG_FAULTS
if(fault_status == FAULT_STATUS_NONE){
// useful for debugging, fade breaks = panda is overloaded #endif
for (int div_mode_loop = 0; div_mode_loop < div_mode; div_mode_loop++) { int div_mode = ((usb_power_mode == USB_POWER_DCP) ? 4 : 1);
for (int fade = 0; fade < 1024; fade += 8) {
for (int i = 0; i < (128/div_mode); i++) { // useful for debugging, fade breaks = panda is overloaded
current_board->set_led(LED_RED, 1); for (int div_mode_loop = 0; div_mode_loop < div_mode; div_mode_loop++) {
if (fade < 512) { delay(fade); } else { delay(1024-fade); } for (int fade = 0; fade < 1024; fade += 8) {
current_board->set_led(LED_RED, 0); for (int i = 0; i < (128/div_mode); i++) {
if (fade < 512) { delay(512-fade); } else { delay(fade-512); } current_board->set_led(LED_RED, 1);
if (fade < 512) { delay(fade); } else { delay(1024-fade); }
current_board->set_led(LED_RED, 0);
if (fade < 512) { delay(512-fade); } else { delay(fade-512); }
}
} }
} }
} #ifdef DEBUG_FAULTS
} else {
current_board->set_led(LED_RED, 1);
delay(512000U);
current_board->set_led(LED_RED, 0);
delay(512000U);
}
#endif
} else { } else {
__WFI(); __WFI();
} }

@ -11,4 +11,5 @@ void can_set_obd(uint8_t harness_orientation, bool obd);
uint8_t hw_type = 0; uint8_t hw_type = 0;
const board *current_board; const board *current_board;
bool is_enumerated = 0; bool is_enumerated = 0;
uint32_t heartbeat_counter = 0; uint32_t heartbeat_counter = 0;
uint32_t uptime_cnt = 0;

@ -3,7 +3,11 @@
#include "libc.h" #include "libc.h"
#include "main_declarations.h" #include "main_declarations.h"
#include "critical.h"
#include "faults.h"
#include "drivers/registers.h"
#include "drivers/interrupts.h"
#include "drivers/llcan.h" #include "drivers/llcan.h"
#include "drivers/llgpio.h" #include "drivers/llgpio.h"
#include "drivers/adc.h" #include "drivers/adc.h"
@ -129,8 +133,7 @@ uint8_t pedal_checksum(uint8_t *dat, int len) {
#define CAN_GAS_SIZE 6 #define CAN_GAS_SIZE 6
#define COUNTER_CYCLE 0xFU #define COUNTER_CYCLE 0xFU
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void CAN1_TX_IRQ_Handler(void) {
void CAN1_TX_IRQHandler(void) {
// clear interrupt // clear interrupt
CAN->TSR |= CAN_TSR_RQCP0; CAN->TSR |= CAN_TSR_RQCP0;
} }
@ -152,8 +155,7 @@ uint32_t current_index = 0;
#define FAULT_INVALID 6U #define FAULT_INVALID 6U
uint8_t state = FAULT_STARTUP; uint8_t state = FAULT_STARTUP;
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void CAN1_RX0_IRQ_Handler(void) {
void CAN1_RX0_IRQHandler(void) {
while ((CAN->RF0R & CAN_RF0R_FMP0) != 0) { while ((CAN->RF0R & CAN_RF0R_FMP0) != 0) {
#ifdef DEBUG #ifdef DEBUG
puts("CAN RX\n"); puts("CAN RX\n");
@ -216,8 +218,7 @@ void CAN1_RX0_IRQHandler(void) {
} }
} }
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void CAN1_SCE_IRQ_Handler(void) {
void CAN1_SCE_IRQHandler(void) {
state = FAULT_SCE; state = FAULT_SCE;
llcan_clear_send(CAN); llcan_clear_send(CAN);
} }
@ -228,8 +229,7 @@ unsigned int pkt_idx = 0;
int led_value = 0; int led_value = 0;
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void TIM3_IRQ_Handler(void) {
void TIM3_IRQHandler(void) {
#ifdef DEBUG #ifdef DEBUG
puth(TIM3->CNT); puth(TIM3->CNT);
puts(" "); puts(" ");
@ -296,6 +296,16 @@ void pedal(void) {
} }
int main(void) { int main(void) {
// Init interrupt table
init_interrupts(true);
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)
disable_interrupts(); disable_interrupts();
// init devices // init devices

@ -1,3 +1,6 @@
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes.
// See rule: CoU_3
#define POWER_SAVE_STATUS_DISABLED 0 #define POWER_SAVE_STATUS_DISABLED 0
#define POWER_SAVE_STATUS_ENABLED 1 #define POWER_SAVE_STATUS_ENABLED 1

@ -18,7 +18,7 @@
#include "safety/safety_elm327.h" #include "safety/safety_elm327.h"
// from cereal.car.CarParams.SafetyModel // from cereal.car.CarParams.SafetyModel
#define SAFETY_NOOUTPUT 0U #define SAFETY_SILENT 0U
#define SAFETY_HONDA 1U #define SAFETY_HONDA 1U
#define SAFETY_TOYOTA 2U #define SAFETY_TOYOTA 2U
#define SAFETY_ELM327 3U #define SAFETY_ELM327 3U
@ -35,8 +35,9 @@
#define SAFETY_TOYOTA_IPAS 16U #define SAFETY_TOYOTA_IPAS 16U
#define SAFETY_ALLOUTPUT 17U #define SAFETY_ALLOUTPUT 17U
#define SAFETY_GM_ASCM 18U #define SAFETY_GM_ASCM 18U
#define SAFETY_NOOUTPUT 19U
uint16_t current_safety_mode = SAFETY_NOOUTPUT; uint16_t current_safety_mode = SAFETY_SILENT;
const safety_hooks *current_hooks = &nooutput_hooks; const safety_hooks *current_hooks = &nooutput_hooks;
void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push){ void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push){
@ -55,39 +56,54 @@ int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
return current_hooks->fwd(bus_num, to_fwd); return current_hooks->fwd(bus_num, to_fwd);
} }
bool addr_allowed(int addr, int bus, const AddrBus addr_list[], int len) {
bool allowed = false;
for (int i = 0; i < len; i++) {
if ((addr == addr_list[i].addr) && (bus == addr_list[i].bus)) {
allowed = true;
break;
}
}
return allowed;
}
typedef struct { typedef struct {
uint16_t id; uint16_t id;
const safety_hooks *hooks; const safety_hooks *hooks;
} safety_hook_config; } safety_hook_config;
const safety_hook_config safety_hook_registry[] = { const safety_hook_config safety_hook_registry[] = {
{SAFETY_NOOUTPUT, &nooutput_hooks}, {SAFETY_SILENT, &nooutput_hooks},
{SAFETY_HONDA, &honda_hooks}, {SAFETY_HONDA, &honda_hooks},
{SAFETY_TOYOTA, &toyota_hooks}, {SAFETY_TOYOTA, &toyota_hooks},
{SAFETY_ELM327, &elm327_hooks}, {SAFETY_ELM327, &elm327_hooks},
{SAFETY_GM, &gm_hooks}, {SAFETY_GM, &gm_hooks},
{SAFETY_HONDA_BOSCH, &honda_bosch_hooks}, {SAFETY_HONDA_BOSCH, &honda_bosch_hooks},
{SAFETY_FORD, &ford_hooks},
{SAFETY_CADILLAC, &cadillac_hooks},
{SAFETY_HYUNDAI, &hyundai_hooks}, {SAFETY_HYUNDAI, &hyundai_hooks},
{SAFETY_CHRYSLER, &chrysler_hooks}, {SAFETY_CHRYSLER, &chrysler_hooks},
{SAFETY_TESLA, &tesla_hooks},
{SAFETY_SUBARU, &subaru_hooks}, {SAFETY_SUBARU, &subaru_hooks},
{SAFETY_MAZDA, &mazda_hooks}, {SAFETY_MAZDA, &mazda_hooks},
{SAFETY_VOLKSWAGEN, &volkswagen_hooks}, {SAFETY_VOLKSWAGEN, &volkswagen_hooks},
{SAFETY_NOOUTPUT, &nooutput_hooks},
#ifdef ALLOW_DEBUG
{SAFETY_CADILLAC, &cadillac_hooks},
{SAFETY_TOYOTA_IPAS, &toyota_ipas_hooks}, {SAFETY_TOYOTA_IPAS, &toyota_ipas_hooks},
{SAFETY_TESLA, &tesla_hooks},
{SAFETY_ALLOUTPUT, &alloutput_hooks}, {SAFETY_ALLOUTPUT, &alloutput_hooks},
{SAFETY_GM_ASCM, &gm_ascm_hooks}, {SAFETY_GM_ASCM, &gm_ascm_hooks},
{SAFETY_FORD, &ford_hooks},
#endif
}; };
int safety_set_mode(uint16_t mode, int16_t param) { int set_safety_hooks(uint16_t mode, int16_t param) {
int set_status = -1; // not set safety_mode_cnt = 0U; // reset safety mode timer
int set_status = -1; // not set
int hook_config_count = sizeof(safety_hook_registry) / sizeof(safety_hook_config); int hook_config_count = sizeof(safety_hook_registry) / sizeof(safety_hook_config);
for (int i = 0; i < hook_config_count; i++) { for (int i = 0; i < hook_config_count; i++) {
if (safety_hook_registry[i].id == mode) { if (safety_hook_registry[i].id == mode) {
current_hooks = safety_hook_registry[i].hooks; current_hooks = safety_hook_registry[i].hooks;
current_safety_mode = safety_hook_registry[i].id; current_safety_mode = safety_hook_registry[i].id;
set_status = 0; // set set_status = 0; // set
break; break;
} }
} }
@ -205,7 +221,7 @@ float interpolate(struct lookup_t xy, float x) {
float y0 = xy.y[i]; float y0 = xy.y[i];
float dx = xy.x[i+1] - x0; float dx = xy.x[i+1] - x0;
float dy = xy.y[i+1] - y0; float dy = xy.y[i+1] - y0;
// dx should not be zero as xy.x is supposed ot be monotonic // dx should not be zero as xy.x is supposed to be monotonic
if (dx <= 0.) { if (dx <= 0.) {
dx = 0.0001; dx = 0.0001;
} }

@ -1,5 +1,6 @@
#define CADILLAC_TORQUE_MSG_N 4 // 4 torque messages: 0x151, 0x152, 0x153, 0x154 #define CADILLAC_TORQUE_MSG_N 4 // 4 torque messages: 0x151, 0x152, 0x153, 0x154
const AddrBus CADILLAC_TX_MSGS[] = {{0x151, 2}, {0x152, 0}, {0x153, 2}, {0x154, 0}};
const int CADILLAC_MAX_STEER = 150; // 1s const int CADILLAC_MAX_STEER = 150; // 1s
// real time torque limit to prevent controls spamming // real time torque limit to prevent controls spamming
// the real time limit is 1500/sec // the real time limit is 1500/sec
@ -55,6 +56,11 @@ static void cadillac_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
static int cadillac_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int cadillac_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int tx = 1; int tx = 1;
int addr = GET_ADDR(to_send); int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send);
if (!addr_allowed(addr, bus, CADILLAC_TX_MSGS, sizeof(CADILLAC_TX_MSGS) / sizeof(CADILLAC_TX_MSGS[0]))) {
tx = 0;
}
// steer cmd checks // steer cmd checks
if ((addr == 0x151) || (addr == 0x152) || (addr == 0x153) || (addr == 0x154)) { if ((addr == 0x151) || (addr == 0x152) || (addr == 0x153) || (addr == 0x154)) {
@ -109,13 +115,8 @@ static int cadillac_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
return tx; return tx;
} }
static void cadillac_init(int16_t param) {
UNUSED(param);
controls_allowed = 0;
}
const safety_hooks cadillac_hooks = { const safety_hooks cadillac_hooks = {
.init = cadillac_init, .init = nooutput_init,
.rx = cadillac_rx_hook, .rx = cadillac_rx_hook,
.tx = cadillac_tx_hook, .tx = cadillac_tx_hook,
.tx_lin = nooutput_tx_lin_hook, .tx_lin = nooutput_tx_lin_hook,

@ -4,8 +4,8 @@ const uint32_t CHRYSLER_RT_INTERVAL = 250000; // 250ms between real time checks
const int CHRYSLER_MAX_RATE_UP = 3; const int CHRYSLER_MAX_RATE_UP = 3;
const int CHRYSLER_MAX_RATE_DOWN = 3; const int CHRYSLER_MAX_RATE_DOWN = 3;
const int CHRYSLER_MAX_TORQUE_ERROR = 80; // max torque cmd in excess of torque motor const int CHRYSLER_MAX_TORQUE_ERROR = 80; // max torque cmd in excess of torque motor
const AddrBus CHRYSLER_TX_MSGS[] = {{571, 0}, {658, 0}, {678, 0}};
bool chrysler_camera_detected = 0; // is giraffe switch 2 high?
int chrysler_rt_torque_last = 0; int chrysler_rt_torque_last = 0;
int chrysler_desired_torque_last = 0; int chrysler_desired_torque_last = 0;
int chrysler_cruise_engaged_last = 0; int chrysler_cruise_engaged_last = 0;
@ -36,23 +36,25 @@ static void chrysler_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
chrysler_cruise_engaged_last = cruise_engaged; chrysler_cruise_engaged_last = cruise_engaged;
} }
// check if stock camera ECU is still online // check if stock camera ECU is on bus 0
if ((bus == 0) && (addr == 0x292)) { if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && (addr == 0x292)) {
chrysler_camera_detected = 1; relay_malfunction = true;
controls_allowed = 0;
} }
} }
static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int tx = 1; int tx = 1;
int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send);
// If camera is on bus 0, then nothing can be sent if (!addr_allowed(addr, bus, CHRYSLER_TX_MSGS, sizeof(CHRYSLER_TX_MSGS) / sizeof(CHRYSLER_TX_MSGS[0]))) {
if (chrysler_camera_detected) {
tx = 0; tx = 0;
} }
int addr = GET_ADDR(to_send); if (relay_malfunction) {
tx = 0;
}
// LKA STEER // LKA STEER
if (addr == 0x292) { if (addr == 0x292) {
@ -100,39 +102,37 @@ static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
} }
} }
// FORCE CANCEL: safety check only relevant when spamming the cancel button. // FORCE CANCEL: only the cancel button press is allowed
// ensuring that only the cancel button press is sent when controls are off. if (addr == 571) {
// This avoids unintended engagements while still allowing resume spam if (GET_BYTE(to_send, 0) != 1) {
// TODO: fix bug preventing the button msg to be fwd'd on bus 2 tx = 0;
}
}
// 1 allows the message through
return tx; return tx;
} }
static void chrysler_init(int16_t param) {
UNUSED(param);
controls_allowed = 0;
chrysler_camera_detected = 0;
}
static int chrysler_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { static int chrysler_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
int bus_fwd = -1; int bus_fwd = -1;
int addr = GET_ADDR(to_fwd); int addr = GET_ADDR(to_fwd);
// forward CAN 0 -> 2 so stock LKAS camera sees messages
if ((bus_num == 0) && !chrysler_camera_detected) { if (!relay_malfunction) {
bus_fwd = 2; // forward CAN 0 -> 2 so stock LKAS camera sees messages
} if (bus_num == 0) {
// forward all messages from camera except LKAS_COMMAND and LKAS_HUD bus_fwd = 2;
if ((bus_num == 2) && !chrysler_camera_detected && (addr != 658) && (addr != 678)) { }
bus_fwd = 0; // forward all messages from camera except LKAS_COMMAND and LKAS_HUD
if ((bus_num == 2) && (addr != 658) && (addr != 678)) {
bus_fwd = 0;
}
} }
return bus_fwd; return bus_fwd;
} }
const safety_hooks chrysler_hooks = { const safety_hooks chrysler_hooks = {
.init = chrysler_init, .init = nooutput_init,
.rx = chrysler_rx_hook, .rx = chrysler_rx_hook,
.tx = chrysler_tx_hook, .tx = chrysler_tx_hook,
.tx_lin = nooutput_tx_lin_hook, .tx_lin = nooutput_tx_lin_hook,

@ -6,7 +6,8 @@ void default_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
static void nooutput_init(int16_t param) { static void nooutput_init(int16_t param) {
UNUSED(param); UNUSED(param);
controls_allowed = 0; controls_allowed = false;
relay_malfunction = false;
} }
static int nooutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int nooutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
@ -39,7 +40,8 @@ const safety_hooks nooutput_hooks = {
static void alloutput_init(int16_t param) { static void alloutput_init(int16_t param) {
UNUSED(param); UNUSED(param);
controls_allowed = 1; controls_allowed = true;
relay_malfunction = false;
} }
static int alloutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int alloutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {

@ -12,7 +12,7 @@ static int elm327_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
//Check valid 29 bit send addresses for ISO 15765-4 //Check valid 29 bit send addresses for ISO 15765-4
//Check valid 11 bit send addresses for ISO 15765-4 //Check valid 11 bit send addresses for ISO 15765-4
if ((addr != 0x18DB33F1) && ((addr & 0x1FFF00FF) != 0x18DA00F1) && if ((addr != 0x18DB33F1) && ((addr & 0x1FFF00FF) != 0x18DA00F1) &&
((addr != 0x7DF) && ((addr & 0x7F8) != 0x7E0))) { ((addr != 0x7DF) && ((addr & 0x1FFFFFF8) != 0x7E0))) {
tx = 0; tx = 0;
} }
return tx; return tx;

@ -14,6 +14,7 @@ bool ford_moving = false;
static void ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static void ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int addr = GET_ADDR(to_push); int addr = GET_ADDR(to_push);
int bus = GET_BUS(to_push);
if (addr == 0x217) { if (addr == 0x217) {
// wheel speeds are 14 bits every 16 // wheel speeds are 14 bits every 16
@ -53,6 +54,10 @@ static void ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
} }
ford_gas_prev = gas; ford_gas_prev = gas;
} }
if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && (addr == 0x3CA)) {
relay_malfunction = true;
}
} }
// all commands: just steering // all commands: just steering
@ -64,11 +69,16 @@ static void ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
static int ford_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int ford_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int tx = 1; int tx = 1;
int addr = GET_ADDR(to_send);
// disallow actuator commands if gas or brake (with vehicle moving) are pressed // disallow actuator commands if gas or brake (with vehicle moving) are pressed
// and the the latching controls_allowed flag is True // and the the latching controls_allowed flag is True
int pedal_pressed = ford_gas_prev || (ford_brake_prev && ford_moving); int pedal_pressed = ford_gas_prev || (ford_brake_prev && ford_moving);
bool current_controls_allowed = controls_allowed && !(pedal_pressed); bool current_controls_allowed = controls_allowed && !(pedal_pressed);
int addr = GET_ADDR(to_send);
if (relay_malfunction) {
tx = 0;
}
// STEER: safety check // STEER: safety check
if (addr == 0x3CA) { if (addr == 0x3CA) {
@ -92,6 +102,8 @@ static int ford_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
return tx; return tx;
} }
// TODO: keep camera on bus 2 and make a fwd_hook
const safety_hooks ford_hooks = { const safety_hooks ford_hooks = {
.init = nooutput_init, .init = nooutput_init,
.rx = ford_rx_hook, .rx = ford_rx_hook,

@ -18,19 +18,21 @@ const int GM_DRIVER_TORQUE_FACTOR = 4;
const int GM_MAX_GAS = 3072; const int GM_MAX_GAS = 3072;
const int GM_MAX_REGEN = 1404; const int GM_MAX_REGEN = 1404;
const int GM_MAX_BRAKE = 350; const int GM_MAX_BRAKE = 350;
const AddrBus GM_TX_MSGS[] = {{384, 0}, {1033, 0}, {1034, 0}, {715, 0}, {880, 0}, // pt bus
{161, 1}, {774, 1}, {776, 1}, {784, 1}, // obs bus
{789, 2}, // ch bus
{0x104c006c, 3}, {0x10400060, 3}}; // gmlan
int gm_brake_prev = 0; int gm_brake_prev = 0;
int gm_gas_prev = 0; int gm_gas_prev = 0;
bool gm_moving = false; bool gm_moving = false;
// silence everything if stock car control ECUs are still online
bool gm_ascm_detected = 0;
int gm_rt_torque_last = 0; int gm_rt_torque_last = 0;
int gm_desired_torque_last = 0; int gm_desired_torque_last = 0;
uint32_t gm_ts_last = 0; uint32_t gm_ts_last = 0;
struct sample_t gm_torque_driver; // last few driver torques measured struct sample_t gm_torque_driver; // last few driver torques measured
static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int bus_number = GET_BUS(to_push); int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push); int addr = GET_ADDR(to_push);
if (addr == 388) { if (addr == 388) {
@ -46,15 +48,6 @@ static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
gm_moving = GET_BYTE(to_push, 0) | GET_BYTE(to_push, 1); gm_moving = GET_BYTE(to_push, 0) | GET_BYTE(to_push, 1);
} }
// Check if ASCM or LKA camera are online
// on powertrain bus.
// 384 = ASCMLKASteeringCmd
// 715 = ASCMGasRegenCmd
if ((bus_number == 0) && ((addr == 384) || (addr == 715))) {
gm_ascm_detected = 1;
controls_allowed = 0;
}
// ACC steering wheel buttons // ACC steering wheel buttons
if (addr == 481) { if (addr == 481) {
int button = (GET_BYTE(to_push, 5) & 0x70) >> 4; int button = (GET_BYTE(to_push, 5) & 0x70) >> 4;
@ -102,6 +95,14 @@ static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
controls_allowed = 0; controls_allowed = 0;
} }
} }
// Check if ASCM or LKA camera are online
// on powertrain bus.
// 384 = ASCMLKASteeringCmd
// 715 = ASCMGasRegenCmd
if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && ((addr == 384) || (addr == 715))) {
relay_malfunction = true;
}
} }
// all commands: gas/regen, friction brake and steering // all commands: gas/regen, friction brake and steering
@ -113,9 +114,14 @@ static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int tx = 1; int tx = 1;
int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send);
if (!addr_allowed(addr, bus, GM_TX_MSGS, sizeof(GM_TX_MSGS)/sizeof(GM_TX_MSGS[0]))) {
tx = 0;
}
// There can be only one! (ASCM) if (relay_malfunction) {
if (gm_ascm_detected) {
tx = 0; tx = 0;
} }
@ -124,8 +130,6 @@ static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int pedal_pressed = gm_gas_prev || (gm_brake_prev && gm_moving); int pedal_pressed = gm_gas_prev || (gm_brake_prev && gm_moving);
bool current_controls_allowed = controls_allowed && !pedal_pressed; bool current_controls_allowed = controls_allowed && !pedal_pressed;
int addr = GET_ADDR(to_send);
// BRAKE: safety check // BRAKE: safety check
if (addr == 789) { if (addr == 789) {
int brake = ((GET_BYTE(to_send, 0) & 0xFU) << 8) + GET_BYTE(to_send, 1); int brake = ((GET_BYTE(to_send, 0) & 0xFU) << 8) + GET_BYTE(to_send, 1);
@ -188,11 +192,6 @@ static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
} }
} }
// PARK ASSIST STEER: unlimited torque, no thanks
if (addr == 823) {
tx = 0;
}
// GAS/REGEN: safety check // GAS/REGEN: safety check
if (addr == 715) { if (addr == 715) {
int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3); int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3);
@ -213,14 +212,9 @@ static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
return tx; return tx;
} }
static void gm_init(int16_t param) {
UNUSED(param);
controls_allowed = 0;
}
const safety_hooks gm_hooks = { const safety_hooks gm_hooks = {
.init = gm_init, .init = nooutput_init,
.rx = gm_rx_hook, .rx = gm_rx_hook,
.tx = gm_tx_hook, .tx = gm_tx_hook,
.tx_lin = nooutput_tx_lin_hook, .tx_lin = nooutput_tx_lin_hook,

@ -6,7 +6,9 @@
// accel rising edge // accel rising edge
// brake rising edge // brake rising edge
// brake > 0mph // brake > 0mph
const AddrBus HONDA_N_TX_MSGS[] = {{0xE4, 0}, {0x194, 0}, {0x1FA, 0}, {0x200, 0}, {0x30C, 0}, {0x33D, 0}, {0x39F, 0}};
const AddrBus HONDA_BH_TX_MSGS[] = {{0xE4, 0}, {0x296, 1}, {0x33D, 0}}; // Bosch Harness
const AddrBus HONDA_BG_TX_MSGS[] = {{0xE4, 2}, {0x296, 0}, {0x33D, 2}}; // Bosch Giraffe
const int HONDA_GAS_INTERCEPTOR_THRESHOLD = 328; // ratio between offset and gain from dbc file const int HONDA_GAS_INTERCEPTOR_THRESHOLD = 328; // ratio between offset and gain from dbc file
int honda_brake = 0; int honda_brake = 0;
int honda_gas_prev = 0; int honda_gas_prev = 0;
@ -95,7 +97,17 @@ static void honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
} else if (honda_stock_brake >= honda_brake) { } else if (honda_stock_brake >= honda_brake) {
honda_fwd_brake = true; honda_fwd_brake = true;
} else { } else {
// Leave honda forward brake as is // Leave Honda forward brake as is
}
}
// if steering controls messages are received on the destination bus, it's an indication
// that the relay might be malfunctioning
int bus_rdr_car = (board_has_relay()) ? 0 : 2; // radar bus, car side
if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && ((addr == 0xE4) || (addr == 0x194))) {
if ((honda_bosch_hardware && (bus == bus_rdr_car)) ||
(!honda_bosch_hardware && (bus == 0))) {
relay_malfunction = true;
} }
} }
} }
@ -112,6 +124,22 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int addr = GET_ADDR(to_send); int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send); int bus = GET_BUS(to_send);
if (honda_bosch_hardware) {
if (board_has_relay() && !addr_allowed(addr, bus, HONDA_BH_TX_MSGS, sizeof(HONDA_BH_TX_MSGS)/sizeof(HONDA_BH_TX_MSGS[0]))) {
tx = 0;
}
if (!board_has_relay() && !addr_allowed(addr, bus, HONDA_BG_TX_MSGS, sizeof(HONDA_BG_TX_MSGS)/sizeof(HONDA_BG_TX_MSGS[0]))) {
tx = 0;
}
}
if (!honda_bosch_hardware && !addr_allowed(addr, bus, HONDA_N_TX_MSGS, sizeof(HONDA_N_TX_MSGS)/sizeof(HONDA_N_TX_MSGS[0]))) {
tx = 0;
}
if (relay_malfunction) {
tx = 0;
}
// disallow actuator commands if gas or brake (with vehicle moving) are pressed // disallow actuator commands if gas or brake (with vehicle moving) are pressed
// and the the latching controls_allowed flag is True // and the the latching controls_allowed flag is True
int pedal_pressed = honda_gas_prev || (gas_interceptor_prev > HONDA_GAS_INTERCEPTOR_THRESHOLD) || int pedal_pressed = honda_gas_prev || (gas_interceptor_prev > HONDA_GAS_INTERCEPTOR_THRESHOLD) ||
@ -170,13 +198,15 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
static void honda_init(int16_t param) { static void honda_init(int16_t param) {
UNUSED(param); UNUSED(param);
controls_allowed = 0; controls_allowed = false;
relay_malfunction = false;
honda_bosch_hardware = false; honda_bosch_hardware = false;
honda_alt_brake_msg = false; honda_alt_brake_msg = false;
} }
static void honda_bosch_init(int16_t param) { static void honda_bosch_init(int16_t param) {
controls_allowed = 0; controls_allowed = false;
relay_malfunction = false;
honda_bosch_hardware = true; honda_bosch_hardware = true;
// Checking for alternate brake override from safety parameter // Checking for alternate brake override from safety parameter
honda_alt_brake_msg = (param == 1) ? true : false; honda_alt_brake_msg = (param == 1) ? true : false;
@ -189,20 +219,22 @@ static int honda_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
// 0x39f is radar hud // 0x39f is radar hud
int bus_fwd = -1; int bus_fwd = -1;
if (bus_num == 0) { if (!relay_malfunction) {
bus_fwd = 2; if (bus_num == 0) {
} bus_fwd = 2;
if (bus_num == 2) { }
// block stock lkas messages and stock acc messages (if OP is doing ACC) if (bus_num == 2) {
int addr = GET_ADDR(to_fwd); // block stock lkas messages and stock acc messages (if OP is doing ACC)
bool is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D); int addr = GET_ADDR(to_fwd);
bool is_acc_hud_msg = (addr == 0x30C) || (addr == 0x39F); bool is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D);
bool is_brake_msg = addr == 0x1FA; bool is_acc_hud_msg = (addr == 0x30C) || (addr == 0x39F);
bool block_fwd = is_lkas_msg || bool is_brake_msg = addr == 0x1FA;
(is_acc_hud_msg && long_controls_allowed) || bool block_fwd = is_lkas_msg ||
(is_brake_msg && long_controls_allowed && !honda_fwd_brake); (is_acc_hud_msg && long_controls_allowed) ||
if (!block_fwd) { (is_brake_msg && long_controls_allowed && !honda_fwd_brake);
bus_fwd = 0; if (!block_fwd) {
bus_fwd = 0;
}
} }
} }
return bus_fwd; return bus_fwd;
@ -213,14 +245,16 @@ static int honda_bosch_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
int bus_rdr_cam = (board_has_relay()) ? 2 : 1; // radar bus, camera side int bus_rdr_cam = (board_has_relay()) ? 2 : 1; // radar bus, camera side
int bus_rdr_car = (board_has_relay()) ? 0 : 2; // radar bus, car side int bus_rdr_car = (board_has_relay()) ? 0 : 2; // radar bus, car side
if (bus_num == bus_rdr_car) { if (!relay_malfunction) {
bus_fwd = bus_rdr_cam; if (bus_num == bus_rdr_car) {
} bus_fwd = bus_rdr_cam;
if (bus_num == bus_rdr_cam) { }
int addr = GET_ADDR(to_fwd); if (bus_num == bus_rdr_cam) {
int is_lkas_msg = (addr == 0xE4) || (addr == 0x33D); int addr = GET_ADDR(to_fwd);
if (!is_lkas_msg) { int is_lkas_msg = (addr == 0xE4) || (addr == 0x33D);
bus_fwd = bus_rdr_car; if (!is_lkas_msg) {
bus_fwd = bus_rdr_car;
}
} }
} }
return bus_fwd; return bus_fwd;

@ -6,9 +6,8 @@ const int HYUNDAI_MAX_RATE_DOWN = 7;
const int HYUNDAI_DRIVER_TORQUE_ALLOWANCE = 50; const int HYUNDAI_DRIVER_TORQUE_ALLOWANCE = 50;
const int HYUNDAI_DRIVER_TORQUE_FACTOR = 2; const int HYUNDAI_DRIVER_TORQUE_FACTOR = 2;
bool hyundai_camera_detected = 0; const AddrBus HYUNDAI_TX_MSGS[] = {{832, 0}, {1265, 0}};
bool hyundai_giraffe_switch_2 = 0; // is giraffe switch 2 high?
int hyundai_camera_bus = 0;
int hyundai_rt_torque_last = 0; int hyundai_rt_torque_last = 0;
int hyundai_desired_torque_last = 0; int hyundai_desired_torque_last = 0;
int hyundai_cruise_engaged_last = 0; int hyundai_cruise_engaged_last = 0;
@ -25,15 +24,9 @@ static void hyundai_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
update_sample(&hyundai_torque_driver, torque_driver_new); update_sample(&hyundai_torque_driver, torque_driver_new);
} }
// check if stock camera ECU is still online // check if stock camera ECU is on bus 0
if ((bus == 0) && (addr == 832)) { if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && (addr == 832)) {
hyundai_camera_detected = 1; relay_malfunction = true;
controls_allowed = 0;
}
// Find out which bus the camera is on
if (addr == 832) {
hyundai_camera_bus = bus;
} }
// enter controls on rising edge of ACC, exit controls on ACC off // enter controls on rising edge of ACC, exit controls on ACC off
@ -48,20 +41,19 @@ static void hyundai_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
} }
hyundai_cruise_engaged_last = cruise_engaged; hyundai_cruise_engaged_last = cruise_engaged;
} }
// 832 is lkas cmd. If it is on camera bus, then giraffe switch 2 is high
if ((addr == 832) && (bus == hyundai_camera_bus) && (hyundai_camera_bus != 0)) {
hyundai_giraffe_switch_2 = 1;
}
} }
static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int tx = 1; int tx = 1;
int addr = GET_ADDR(to_send); int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send);
if (!addr_allowed(addr, bus, HYUNDAI_TX_MSGS, sizeof(HYUNDAI_TX_MSGS)/sizeof(HYUNDAI_TX_MSGS[0]))) {
tx = 0;
}
// There can be only one! (camera) if (relay_malfunction) {
if (hyundai_camera_detected) {
tx = 0; tx = 0;
} }
@ -115,12 +107,11 @@ static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
// FORCE CANCEL: safety check only relevant when spamming the cancel button. // FORCE CANCEL: safety check only relevant when spamming the cancel button.
// ensuring that only the cancel button press is sent (VAL 4) when controls are off. // ensuring that only the cancel button press is sent (VAL 4) when controls are off.
// This avoids unintended engagements while still allowing resume spam // This avoids unintended engagements while still allowing resume spam
// TODO: fix bug preventing the button msg to be fwd'd on bus 2 if ((addr == 1265) && !controls_allowed) {
//if ((addr == 1265) && !controls_allowed && (bus == 0) { if ((GET_BYTES_04(to_send) & 0x7) != 4) {
// if ((GET_BYTES_04(to_send) & 0x7) != 4) { tx = 0;
// tx = 0; }
// } }
//}
// 1 allows the message through // 1 allows the message through
return tx; return tx;
@ -129,29 +120,22 @@ static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
static int hyundai_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { static int hyundai_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
int bus_fwd = -1; int bus_fwd = -1;
int addr = GET_ADDR(to_fwd);
// forward cam to ccan and viceversa, except lkas cmd // forward cam to ccan and viceversa, except lkas cmd
if (hyundai_giraffe_switch_2) { if (!relay_malfunction) {
if (bus_num == 0) { if (bus_num == 0) {
bus_fwd = hyundai_camera_bus; bus_fwd = 2;
} }
if (bus_num == hyundai_camera_bus) { if ((bus_num == 2) && (addr != 832)) {
int addr = GET_ADDR(to_fwd); bus_fwd = 0;
if (addr != 832) {
bus_fwd = 0;
}
} }
} }
return bus_fwd; return bus_fwd;
} }
static void hyundai_init(int16_t param) {
UNUSED(param);
controls_allowed = 0;
hyundai_giraffe_switch_2 = 0;
}
const safety_hooks hyundai_hooks = { const safety_hooks hyundai_hooks = {
.init = hyundai_init, .init = nooutput_init,
.rx = hyundai_rx_hook, .rx = hyundai_rx_hook,
.tx = hyundai_tx_hook, .tx = hyundai_tx_hook,
.tx_lin = nooutput_tx_lin_hook, .tx_lin = nooutput_tx_lin_hook,

@ -30,12 +30,6 @@ uint32_t mazda_ts_last = 0;
struct sample_t mazda_torque_driver; // last few driver torques measured struct sample_t mazda_torque_driver; // last few driver torques measured
// track msgs coming from OP so that we know what CAM msgs to drop and what to forward // track msgs coming from OP so that we know what CAM msgs to drop and what to forward
int mazda_op_lkas_detected = 0;
int mazda_op_laneinfo_detected = 0;
int mazda_forward_cam = 0;
int mazda_giraffe_switch_2_on = 0;
void mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { void mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int bus = GET_BUS(to_push); int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push); int addr = GET_ADDR(to_push);
@ -51,7 +45,7 @@ void mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int cruise_engaged = GET_BYTE(to_push, 0) & 8; int cruise_engaged = GET_BYTE(to_push, 0) & 8;
if (cruise_engaged != 0) { if (cruise_engaged != 0) {
if (!mazda_cruise_engaged_last) { if (!mazda_cruise_engaged_last) {
controls_allowed = 1; controls_allowed = 1;
} }
} }
else { else {
@ -60,17 +54,9 @@ void mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
mazda_cruise_engaged_last = cruise_engaged; mazda_cruise_engaged_last = cruise_engaged;
} }
// we have msgs on bus MAZDA_CAM // if we see wheel speed msgs on MAZDA_CAM bus then relay is closed
if (bus == MAZDA_CAM) { if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == MAZDA_CAM) && (addr == MAZDA_WHEEL_SPEED)) {
// the stock CAM is connected relay_malfunction = true;
if (addr == MAZDA_LKAS) {
mazda_forward_cam = 1;
}
// if we see wheel speed msgs on MAZDA_CAM bus then giraffe switch 2 is high
// (hardware passthru)
if (addr == MAZDA_WHEEL_SPEED) {
mazda_giraffe_switch_2_on = 1;
}
} }
} }
@ -79,15 +65,12 @@ static int mazda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int addr = GET_ADDR(to_send); int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send); int bus = GET_BUS(to_send);
if (relay_malfunction) {
tx = 0;
}
// Check if msg is sent on the main BUS // Check if msg is sent on the main BUS
if (bus == MAZDA_MAIN) { if (bus == MAZDA_MAIN) {
if ((addr == MAZDA_LKAS) && !mazda_op_lkas_detected){
mazda_op_lkas_detected = 1;
}
if ((addr == MAZDA_LANEINFO) && !mazda_op_laneinfo_detected){
mazda_op_laneinfo_detected = 1;
}
// steer cmd checks // steer cmd checks
if (addr == MAZDA_LKAS) { if (addr == MAZDA_LKAS) {
int desired_torque = (((GET_BYTE(to_send, 0) & 0x0f) << 8) | GET_BYTE(to_send, 1)) - MAZDA_MAX_STEER; int desired_torque = (((GET_BYTE(to_send, 0) & 0x0f) << 8) | GET_BYTE(to_send, 1)) - MAZDA_MAX_STEER;
@ -96,42 +79,42 @@ static int mazda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
if (controls_allowed) { if (controls_allowed) {
// *** global torque limit check *** // *** global torque limit check ***
violation |= max_limit_check(desired_torque, MAZDA_MAX_STEER, -MAZDA_MAX_STEER); violation |= max_limit_check(desired_torque, MAZDA_MAX_STEER, -MAZDA_MAX_STEER);
// *** torque rate limit check *** // *** torque rate limit check ***
int desired_torque_last = mazda_desired_torque_last; int desired_torque_last = mazda_desired_torque_last;
violation |= driver_limit_check(desired_torque, desired_torque_last, &mazda_torque_driver, violation |= driver_limit_check(desired_torque, desired_torque_last, &mazda_torque_driver,
MAZDA_MAX_STEER, MAZDA_MAX_RATE_UP, MAZDA_MAX_RATE_DOWN, MAZDA_MAX_STEER, MAZDA_MAX_RATE_UP, MAZDA_MAX_RATE_DOWN,
MAZDA_DRIVER_TORQUE_ALLOWANCE, MAZDA_DRIVER_TORQUE_FACTOR); MAZDA_DRIVER_TORQUE_ALLOWANCE, MAZDA_DRIVER_TORQUE_FACTOR);
// used next time // used next time
mazda_desired_torque_last = desired_torque; mazda_desired_torque_last = desired_torque;
// *** torque real time rate limit check *** // *** torque real time rate limit check ***
violation |= rt_rate_limit_check(desired_torque, mazda_rt_torque_last, MAZDA_MAX_RT_DELTA); violation |= rt_rate_limit_check(desired_torque, mazda_rt_torque_last, MAZDA_MAX_RT_DELTA);
// every RT_INTERVAL set the new limits // every RT_INTERVAL set the new limits
uint32_t ts_elapsed = get_ts_elapsed(ts, mazda_ts_last); uint32_t ts_elapsed = get_ts_elapsed(ts, mazda_ts_last);
if (ts_elapsed > ((uint32_t) MAZDA_RT_INTERVAL)) { if (ts_elapsed > ((uint32_t) MAZDA_RT_INTERVAL)) {
mazda_rt_torque_last = desired_torque; mazda_rt_torque_last = desired_torque;
mazda_ts_last = ts; mazda_ts_last = ts;
} }
} }
// no torque if controls is not allowed // no torque if controls is not allowed
if (!controls_allowed && (desired_torque != 0)) { if (!controls_allowed && (desired_torque != 0)) {
violation = 1; violation = 1;
} }
// reset to 0 if either controls is not allowed or there's a violation // reset to 0 if either controls is not allowed or there's a violation
if (violation || !controls_allowed) { if (violation || !controls_allowed) {
mazda_desired_torque_last = 0; mazda_desired_torque_last = 0;
mazda_rt_torque_last = 0; mazda_rt_torque_last = 0;
mazda_ts_last = ts; mazda_ts_last = ts;
} }
if (violation) { if (violation) {
tx = 0; tx = 0;
} }
} }
} }
@ -140,16 +123,14 @@ static int mazda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
static int mazda_fwd_hook(int bus, CAN_FIFOMailBox_TypeDef *to_fwd) { static int mazda_fwd_hook(int bus, CAN_FIFOMailBox_TypeDef *to_fwd) {
int bus_fwd = -1; int bus_fwd = -1;
if (mazda_forward_cam && !mazda_giraffe_switch_2_on) { if (!relay_malfunction) {
int addr = GET_ADDR(to_fwd); int addr = GET_ADDR(to_fwd);
if (bus == MAZDA_MAIN) { if (bus == MAZDA_MAIN) {
bus_fwd = MAZDA_CAM; bus_fwd = MAZDA_CAM;
} }
else if (bus == MAZDA_CAM) { else if (bus == MAZDA_CAM) {
// drop stock CAM_LKAS and CAM_LANEINFI if OP is sending them if (!(addr == MAZDA_LKAS)) {
if (!((addr == MAZDA_LKAS) && mazda_op_lkas_detected) && bus_fwd = MAZDA_MAIN;
!((addr == MAZDA_LANEINFO) && mazda_op_laneinfo_detected)) {
bus_fwd = MAZDA_MAIN;
} }
} }
else { else {

@ -8,6 +8,8 @@ const int SUBARU_MAX_RATE_DOWN = 70;
const int SUBARU_DRIVER_TORQUE_ALLOWANCE = 60; const int SUBARU_DRIVER_TORQUE_ALLOWANCE = 60;
const int SUBARU_DRIVER_TORQUE_FACTOR = 10; const int SUBARU_DRIVER_TORQUE_FACTOR = 10;
const AddrBus SUBARU_TX_MSGS[] = {{0x122, 0}, {0x164, 0}, {0x221, 0}, {0x322, 0}};
int subaru_cruise_engaged_last = 0; int subaru_cruise_engaged_last = 0;
int subaru_rt_torque_last = 0; int subaru_rt_torque_last = 0;
int subaru_desired_torque_last = 0; int subaru_desired_torque_last = 0;
@ -38,11 +40,24 @@ static void subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
} }
subaru_cruise_engaged_last = cruise_engaged; subaru_cruise_engaged_last = cruise_engaged;
} }
if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && ((addr == 0x122) || (addr == 0x164))) {
relay_malfunction = true;
}
} }
static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int tx = 1; int tx = 1;
int addr = GET_ADDR(to_send); int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send);
if (!addr_allowed(addr, bus, SUBARU_TX_MSGS, sizeof(SUBARU_TX_MSGS) / sizeof(SUBARU_TX_MSGS[0]))) {
tx = 0;
}
if (relay_malfunction) {
tx = 0;
}
// steer cmd checks // steer cmd checks
if ((addr == 0x122) || (addr == 0x164)) { if ((addr == 0x122) || (addr == 0x164)) {
@ -98,23 +113,24 @@ static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
} }
static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
int bus_fwd = -1; int bus_fwd = -1;
if (bus_num == 0) {
bus_fwd = 2; // Camera CAN if (!relay_malfunction) {
} if (bus_num == 0) {
if (bus_num == 2) { bus_fwd = 2; // Camera CAN
// 290 is LKAS for Global Platform }
// 356 is LKAS for outback 2015 if (bus_num == 2) {
// 545 is ES_Distance // 290 is LKAS for Global Platform
// 802 is ES_LKAS // 356 is LKAS for outback 2015
int addr = GET_ADDR(to_fwd); // 545 is ES_Distance
int block_msg = (addr == 290) || (addr == 356) || (addr == 545) || (addr == 802); // 802 is ES_LKAS
if (!block_msg) { int addr = GET_ADDR(to_fwd);
bus_fwd = 0; // Main CAN int block_msg = (addr == 290) || (addr == 356) || (addr == 545) || (addr == 802);
if (!block_msg) {
bus_fwd = 0; // Main CAN
}
} }
} }
// fallback to do not forward // fallback to do not forward
return bus_fwd; return bus_fwd;
} }

@ -18,12 +18,16 @@ const int TOYOTA_MIN_ACCEL = -3000; // 3.0 m/s2
const int TOYOTA_GAS_INTERCEPTOR_THRESHOLD = 475; // ratio between offset and gain from dbc file const int TOYOTA_GAS_INTERCEPTOR_THRESHOLD = 475; // ratio between offset and gain from dbc file
// allowed DSU messages on bus 0 and 1
const AddrBus TOYOTA_TX_MSGS[] = {{0x283, 0}, {0x2E6, 0}, {0x2E7, 0}, {0x33E, 0}, {0x344, 0}, {0x365, 0}, {0x366, 0}, {0x4CB, 0}, // DSU bus 0
{0x128, 1}, {0x141, 1}, {0x160, 1}, {0x161, 1}, {0x470, 1}, // DSU bus 1
{0x2E4, 0}, {0x411, 0}, {0x412, 0}, {0x343, 0}, {0x1D2, 0}, // LKAS + ACC
{0x200, 0}}; // interceptor
// global actuation limit states // global actuation limit states
int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file
// states // states
int toyota_giraffe_switch_1 = 0; // is giraffe switch 1 high?
int toyota_camera_forwarded = 0; // should we forward the camera bus?
int toyota_desired_torque_last = 0; // last desired steer torque int toyota_desired_torque_last = 0; // last desired steer torque
int toyota_rt_torque_last = 0; // last desired torque for real time check int toyota_rt_torque_last = 0; // last desired torque for real time check
uint32_t toyota_ts_last = 0; uint32_t toyota_ts_last = 0;
@ -87,14 +91,9 @@ static void toyota_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
toyota_gas_prev = gas; toyota_gas_prev = gas;
} }
// msgs are only on bus 2 if panda is connected to frc // 0x2E4 is lkas cmd. If it is on bus 0, then relay is unexpectedly closed
if (bus == 2) { if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (addr == 0x2E4) && (bus == 0)) {
toyota_camera_forwarded = 1; relay_malfunction = true;
}
// 0x2E4 is lkas cmd. If it is on bus 0, then giraffe switch 1 is high
if ((addr == 0x2E4) && (bus == 0)) {
toyota_giraffe_switch_1 = 1;
} }
} }
@ -104,14 +103,17 @@ static int toyota_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int addr = GET_ADDR(to_send); int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send); int bus = GET_BUS(to_send);
if (!addr_allowed(addr, bus, TOYOTA_TX_MSGS, sizeof(TOYOTA_TX_MSGS)/sizeof(TOYOTA_TX_MSGS[0]))) {
tx = 0;
}
if (relay_malfunction) {
tx = 0;
}
// Check if msg is sent on BUS 0 // Check if msg is sent on BUS 0
if (bus == 0) { if (bus == 0) {
// no IPAS in non IPAS mode
if ((addr == 0x266) || (addr == 0x167)) {
tx = 0;
}
// GAS PEDAL: safety check // GAS PEDAL: safety check
if (addr == 0x200) { if (addr == 0x200) {
if (!controls_allowed || !long_controls_allowed) { if (!controls_allowed || !long_controls_allowed) {
@ -185,28 +187,26 @@ static int toyota_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
} }
} }
// 1 allows the message through
return tx; return tx;
} }
static void toyota_init(int16_t param) { static void toyota_init(int16_t param) {
controls_allowed = 0; controls_allowed = 0;
toyota_giraffe_switch_1 = 0; relay_malfunction = 0;
toyota_camera_forwarded = 0;
toyota_dbc_eps_torque_factor = param; toyota_dbc_eps_torque_factor = param;
} }
static int toyota_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { static int toyota_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
int bus_fwd = -1; int bus_fwd = -1;
if (toyota_camera_forwarded && !toyota_giraffe_switch_1) { if (!relay_malfunction) {
if (bus_num == 0) { if (bus_num == 0) {
bus_fwd = 2; bus_fwd = 2;
} }
if (bus_num == 2) { if (bus_num == 2) {
int addr = GET_ADDR(to_fwd); int addr = GET_ADDR(to_fwd);
// block stock lkas messages and stock acc messages (if OP is doing ACC) // block stock lkas messages and stock acc messages (if OP is doing ACC)
// in TSS2, 0.191 is LTA which we need to block to avoid controls collision // in TSS2, 0x191 is LTA which we need to block to avoid controls collision
int is_lkas_msg = ((addr == 0x2E4) || (addr == 0x412) || (addr == 0x191)); int is_lkas_msg = ((addr == 0x2E4) || (addr == 0x412) || (addr == 0x191));
// in TSS2 the camera does ACC as well, so filter 0x343 // in TSS2 the camera does ACC as well, so filter 0x343
int is_acc_msg = (addr == 0x343); int is_acc_msg = (addr == 0x343);

@ -1,3 +1,11 @@
// Safety-relevant CAN messages for the Volkswagen MQB platform.
#define MSG_EPS_01 0x09F
#define MSG_MOTOR_20 0x121
#define MSG_ACC_06 0x122
#define MSG_HCA_01 0x126
#define MSG_GRA_ACC_01 0x12B
#define MSG_LDW_02 0x397
const int VOLKSWAGEN_MAX_STEER = 250; // 2.5 Nm (EPS side max of 3.0Nm with fault if violated) const int VOLKSWAGEN_MAX_STEER = 250; // 2.5 Nm (EPS side max of 3.0Nm with fault if violated)
const int VOLKSWAGEN_MAX_RT_DELTA = 75; // 4 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 50 ; 50 * 1.5 for safety pad = 75 const int VOLKSWAGEN_MAX_RT_DELTA = 75; // 4 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 50 ; 50 * 1.5 for safety pad = 75
const uint32_t VOLKSWAGEN_RT_INTERVAL = 250000; // 250ms between real time checks const uint32_t VOLKSWAGEN_RT_INTERVAL = 250000; // 250ms between real time checks
@ -6,26 +14,15 @@ const int VOLKSWAGEN_MAX_RATE_DOWN = 10; // 5.0 Nm/s available rate o
const int VOLKSWAGEN_DRIVER_TORQUE_ALLOWANCE = 80; const int VOLKSWAGEN_DRIVER_TORQUE_ALLOWANCE = 80;
const int VOLKSWAGEN_DRIVER_TORQUE_FACTOR = 3; const int VOLKSWAGEN_DRIVER_TORQUE_FACTOR = 3;
// MSG_GRA_ACC_01 is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration
const AddrBus VOLKSWAGEN_TX_MSGS[] = {{MSG_HCA_01, 0}, {MSG_GRA_ACC_01, 0}, {MSG_GRA_ACC_01, 2}, {MSG_LDW_02, 0}};
struct sample_t volkswagen_torque_driver; // last few driver torques measured struct sample_t volkswagen_torque_driver; // last few driver torques measured
int volkswagen_rt_torque_last = 0; int volkswagen_rt_torque_last = 0;
int volkswagen_desired_torque_last = 0; int volkswagen_desired_torque_last = 0;
uint32_t volkswagen_ts_last = 0; uint32_t volkswagen_ts_last = 0;
int volkswagen_gas_prev = 0; int volkswagen_gas_prev = 0;
// Safety-relevant CAN messages for the Volkswagen MQB platform.
#define MSG_EPS_01 0x09F
#define MSG_MOTOR_20 0x121
#define MSG_ACC_06 0x122
#define MSG_HCA_01 0x126
#define MSG_GRA_ACC_01 0x12B
#define MSG_LDW_02 0x397
#define MSG_KLEMMEN_STATUS_01 0x3C0
static void volkswagen_init(int16_t param) {
UNUSED(param); // May use param in the future to indicate MQB vs PQ35/PQ46/NMS vs MLB, or wiring configuration.
controls_allowed = 0;
}
static void volkswagen_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static void volkswagen_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int bus = GET_BUS(to_push); int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push); int addr = GET_ADDR(to_push);
@ -58,6 +55,10 @@ static void volkswagen_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
} }
volkswagen_gas_prev = gas; volkswagen_gas_prev = gas;
} }
if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && (addr == MSG_HCA_01)) {
relay_malfunction = true;
}
} }
static int volkswagen_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int volkswagen_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
@ -65,6 +66,14 @@ static int volkswagen_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int bus = GET_BUS(to_send); int bus = GET_BUS(to_send);
int tx = 1; int tx = 1;
if (!addr_allowed(addr, bus, VOLKSWAGEN_TX_MSGS, sizeof(VOLKSWAGEN_TX_MSGS)/sizeof(VOLKSWAGEN_TX_MSGS[0]))) {
tx = 0;
}
if (relay_malfunction) {
tx = 0;
}
// Safety check for HCA_01 Heading Control Assist torque. // Safety check for HCA_01 Heading Control Assist torque.
if (addr == MSG_HCA_01) { if (addr == MSG_HCA_01) {
bool violation = false; bool violation = false;
@ -118,7 +127,7 @@ static int volkswagen_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
// FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off. // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off.
// This avoids unintended engagements while still allowing resume spam // This avoids unintended engagements while still allowing resume spam
if ((bus == 2) && (addr == MSG_GRA_ACC_01) && !controls_allowed) { if ((addr == MSG_GRA_ACC_01) && !controls_allowed) {
// disallow resume and set: bits 16 and 19 // disallow resume and set: bits 16 and 19
if ((GET_BYTE(to_send, 2) & 0x9) != 0) { if ((GET_BYTE(to_send, 2) & 0x9) != 0) {
tx = 0; tx = 0;
@ -135,31 +144,32 @@ static int volkswagen_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
// NOTE: Will need refactoring for other bus layouts, such as no-forwarding at camera or J533 running-gear CAN // NOTE: Will need refactoring for other bus layouts, such as no-forwarding at camera or J533 running-gear CAN
switch (bus_num) { if (!relay_malfunction) {
case 0: switch (bus_num) {
// Forward all traffic from J533 gateway to Extended CAN devices. case 0:
bus_fwd = 2; // Forward all traffic from J533 gateway to Extended CAN devices.
break; bus_fwd = 2;
case 2: break;
if ((addr == MSG_HCA_01) || (addr == MSG_LDW_02)) { case 2:
// OP takes control of the Heading Control Assist and Lane Departure Warning messages from the camera. if ((addr == MSG_HCA_01) || (addr == MSG_LDW_02)) {
// OP takes control of the Heading Control Assist and Lane Departure Warning messages from the camera.
bus_fwd = -1;
} else {
// Forward all remaining traffic from Extended CAN devices to J533 gateway.
bus_fwd = 0;
}
break;
default:
// No other buses should be in use; fallback to do-not-forward.
bus_fwd = -1; bus_fwd = -1;
} else { break;
// Forward all remaining traffic from Extended CAN devices to J533 gateway. }
bus_fwd = 0;
}
break;
default:
// No other buses should be in use; fallback to do-not-forward.
bus_fwd = -1;
break;
} }
return bus_fwd; return bus_fwd;
} }
const safety_hooks volkswagen_hooks = { const safety_hooks volkswagen_hooks = {
.init = volkswagen_init, .init = nooutput_init,
.rx = volkswagen_rx_hook, .rx = volkswagen_rx_hook,
.tx = volkswagen_tx_hook, .tx = volkswagen_tx_hook,
.tx_lin = nooutput_tx_lin_hook, .tx_lin = nooutput_tx_lin_hook,

@ -11,6 +11,11 @@ struct lookup_t {
float y[3]; float y[3];
}; };
typedef struct {
int addr;
int bus;
} AddrBus;
void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push); void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push);
int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_send); int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_send);
int safety_tx_lin_hook(int lin_num, uint8_t *data, int len); int safety_tx_lin_hook(int lin_num, uint8_t *data, int len);
@ -25,6 +30,7 @@ bool driver_limit_check(int val, int val_last, struct sample_t *val_driver,
const int MAX_ALLOWANCE, const int DRIVER_FACTOR); const int MAX_ALLOWANCE, const int DRIVER_FACTOR);
bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA); bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA);
float interpolate(struct lookup_t xy, float x); float interpolate(struct lookup_t xy, float x);
bool addr_allowed(int addr, int bus, const AddrBus addr_list[], int len);
typedef void (*safety_hook_init)(int16_t param); typedef void (*safety_hook_init)(int16_t param);
typedef void (*rx_hook)(CAN_FIFOMailBox_TypeDef *to_push); typedef void (*rx_hook)(CAN_FIFOMailBox_TypeDef *to_push);
@ -40,13 +46,19 @@ typedef struct {
fwd_hook fwd; fwd_hook fwd;
} safety_hooks; } safety_hooks;
// This can be set by the safety hooks. // This can be set by the safety hooks
bool controls_allowed = 0; bool controls_allowed = false;
bool gas_interceptor_detected = 0; bool relay_malfunction = false;
bool gas_interceptor_detected = false;
int gas_interceptor_prev = 0; int gas_interceptor_prev = 0;
// This is set by USB command 0xdf // This is set by USB command 0xdf
bool long_controls_allowed = 1; bool long_controls_allowed = true;
// time since safety mode has been changed
uint32_t safety_mode_cnt = 0U;
// allow 1s of transition timeout after relay changes state before assessing malfunctioning
const uint32_t RELAY_TRNS_TIMEOUT = 1U;
// avg between 2 tracks // avg between 2 tracks
#define GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + ((GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2 ) / 2) #define GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + ((GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2 ) / 2)

@ -65,7 +65,12 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired)
// so it's blocked over wifi // so it's blocked over wifi
switch (setup->b.wValue.w) { switch (setup->b.wValue.w) {
case 0: case 0:
#ifdef ALLOW_DEBUG
if (hardwired) { if (hardwired) {
#else
// no more bootstub on UNO
if (hardwired && hw_type != HW_TYPE_UNO) {
#endif
puts("-> entering bootloader\n"); puts("-> entering bootloader\n");
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
NVIC_SystemReset(); NVIC_SystemReset();
@ -151,7 +156,7 @@ int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) {
#define CAN_BL_INPUT 0x1 #define CAN_BL_INPUT 0x1
#define CAN_BL_OUTPUT 0x2 #define CAN_BL_OUTPUT 0x2
void CAN1_TX_IRQHandler(void) { void CAN1_TX_IRQ_Handler(void) {
// clear interrupt // clear interrupt
CAN->TSR |= CAN_TSR_RQCP0; CAN->TSR |= CAN_TSR_RQCP0;
} }
@ -178,7 +183,7 @@ void bl_can_send(uint8_t *odat) {
CAN->sTxMailBox[0].TIR = (CAN_BL_OUTPUT << 21) | 1; CAN->sTxMailBox[0].TIR = (CAN_BL_OUTPUT << 21) | 1;
} }
void CAN1_RX0_IRQHandler(void) { void CAN1_RX0_IRQ_Handler(void) {
while (CAN->RF0R & CAN_RF0R_FMP0) { while (CAN->RF0R & CAN_RF0R_FMP0) {
if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) { if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) {
uint8_t dat[8]; uint8_t dat[8];
@ -253,13 +258,19 @@ void CAN1_RX0_IRQHandler(void) {
} }
} }
void CAN1_SCE_IRQHandler(void) { void CAN1_SCE_IRQ_Handler(void) {
llcan_clear_send(CAN); llcan_clear_send(CAN);
} }
#endif #endif
void soft_flasher_start(void) { void soft_flasher_start(void) {
#ifdef PEDAL
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)
#endif
puts("\n\n\n************************ FLASHER START ************************\n"); puts("\n\n\n************************ FLASHER START ************************\n");
enter_bootloader_mode = 0; enter_bootloader_mode = 0;

@ -6,6 +6,9 @@ import hashlib
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
import binascii import binascii
# increment this to make new hardware not run old versions
VERSION = 2
rsa = RSA.importKey(open(sys.argv[3]).read()) rsa = RSA.importKey(open(sys.argv[3]).read())
with open(sys.argv[1], "rb") as f: with open(sys.argv[1], "rb") as f:
@ -15,6 +18,9 @@ print("signing", len(dat), "bytes")
with open(sys.argv[2], "wb") as f: with open(sys.argv[2], "wb") as f:
if os.getenv("SETLEN") is not None: if os.getenv("SETLEN") is not None:
# add the version at the end
dat += b"VERS" + struct.pack("I", VERSION)
# add the length at the beginning
x = struct.pack("I", len(dat)) + dat[4:] x = struct.pack("I", len(dat)) + dat[4:]
# mock signature of dat[4:] # mock signature of dat[4:]
dd = hashlib.sha1(dat[4:]).digest() dd = hashlib.sha1(dat[4:]).digest()

@ -39,7 +39,7 @@
#define PANDA_DLC_MASK 0x0F #define PANDA_DLC_MASK 0x0F
#define SAFETY_ALLOUTPUT 17 #define SAFETY_ALLOUTPUT 17
#define SAFETY_NOOUTPUT 0 #define SAFETY_SILENT 0
struct panda_usb_ctx { struct panda_usb_ctx {
struct panda_inf_priv *priv; struct panda_inf_priv *priv;
@ -159,7 +159,7 @@ static int panda_set_output_enable(struct panda_inf_priv* priv, bool enable){
return usb_control_msg(priv->priv_dev->udev, return usb_control_msg(priv->priv_dev->udev,
usb_sndctrlpipe(priv->priv_dev->udev, 0), usb_sndctrlpipe(priv->priv_dev->udev, 0),
0xDC, USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0xDC, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
enable ? SAFETY_ALLOUTPUT : SAFETY_NOOUTPUT, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); enable ? SAFETY_ALLOUTPUT : SAFETY_SILENT, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
} }
static void panda_usb_write_bulk_callback(struct urb *urb) static void panda_usb_write_bulk_callback(struct urb *urb)

@ -277,7 +277,7 @@ bool Panda::esp_reset(uint16_t bootmode = 0) {
return this->control_transfer(REQUEST_OUT, 0xda, bootmode, 0, NULL, 0, 0) != -1; return this->control_transfer(REQUEST_OUT, 0xda, bootmode, 0, NULL, 0, 0) != -1;
} }
bool Panda::set_safety_mode(PANDA_SAFETY_MODE mode = SAFETY_NOOUTPUT) { bool Panda::set_safety_mode(PANDA_SAFETY_MODE mode = SAFETY_SILENT) {
return this->control_transfer(REQUEST_OUT, 0xdc, mode, 0, NULL, 0, 0) != -1; return this->control_transfer(REQUEST_OUT, 0xdc, mode, 0, NULL, 0, 0) != -1;
} }

@ -38,7 +38,7 @@
namespace panda { namespace panda {
typedef enum _PANDA_SAFETY_MODE : uint16_t { typedef enum _PANDA_SAFETY_MODE : uint16_t {
SAFETY_NOOUTPUT = 0, SAFETY_SILENT = 0,
SAFETY_HONDA = 1, SAFETY_HONDA = 1,
SAFETY_ALLOUTPUT = 17, SAFETY_ALLOUTPUT = 17,
} PANDA_SAFETY_MODE; } PANDA_SAFETY_MODE;

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import binascii
import csv import csv
import sys import sys
from panda import Panda from panda import Panda
@ -21,7 +20,7 @@ def can_logger():
sys.exit(0) sys.exit(0)
try: try:
outputfile = open('output.csv', 'wb') outputfile = open('output.csv', 'w')
csvwriter = csv.writer(outputfile) csvwriter = csv.writer(outputfile)
#Write Header #Write Header
csvwriter.writerow(['Bus', 'MessageID', 'Message', 'MessageLength']) csvwriter.writerow(['Bus', 'MessageID', 'Message', 'MessageLength'])
@ -35,7 +34,7 @@ def can_logger():
can_recv = p.can_recv() can_recv = p.can_recv()
for address, _, dat, src in can_recv: for address, _, dat, src in can_recv:
csvwriter.writerow([str(src), str(hex(address)), "0x" + binascii.hexlify(dat), len(dat)]) csvwriter.writerow([str(src), str(hex(address)), f"0x{dat.hex()}", len(dat)])
if src == 0: if src == 0:
bus0_msg_cnt += 1 bus0_msg_cnt += 1
@ -44,10 +43,10 @@ def can_logger():
elif src == 2: elif src == 2:
bus2_msg_cnt += 1 bus2_msg_cnt += 1
print("Message Counts... Bus 0: " + str(bus0_msg_cnt) + " Bus 1: " + str(bus1_msg_cnt) + " Bus 2: " + str(bus2_msg_cnt), end='\r') print(f"Message Counts... Bus 0: {bus0_msg_cnt} Bus 1: {bus1_msg_cnt} Bus 2: {bus2_msg_cnt}", end='\r')
except KeyboardInterrupt: except KeyboardInterrupt:
print("\nNow exiting. Final message Counts... Bus 0: " + str(bus0_msg_cnt) + " Bus 1: " + str(bus1_msg_cnt) + " Bus 2: " + str(bus2_msg_cnt)) print(f"\nNow exiting. Final message Counts... Bus 0: {bus0_msg_cnt} Bus 1: {bus1_msg_cnt} Bus 2: {bus2_msg_cnt}")
outputfile.close() outputfile.close()
if __name__ == "__main__": if __name__ == "__main__":

@ -1,59 +0,0 @@
#!/usr/bin/env python3
from panda import Panda
from panda.python.uds import UdsClient, NegativeResponseError, DATA_IDENTIFIER_TYPE
if __name__ == "__main__":
address = 0x18da30f1 # Honda EPS
panda = Panda()
uds_client = UdsClient(panda, address, debug=False)
print("tester present ...")
uds_client.tester_present()
try:
print("")
print("read data by id: boot software id ...")
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION)
print(data.decode('utf-8'))
except NegativeResponseError as e:
print(e)
try:
print("")
print("read data by id: application software id ...")
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION)
print(data.decode('utf-8'))
except NegativeResponseError as e:
print(e)
try:
print("")
print("read data by id: application data id ...")
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
print(data.decode('utf-8'))
except NegativeResponseError as e:
print(e)
try:
print("")
print("read data by id: boot software fingerprint ...")
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT)
print(data.decode('utf-8'))
except NegativeResponseError as e:
print(e)
try:
print("")
print("read data by id: application software fingerprint ...")
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT)
print(data.decode('utf-8'))
except NegativeResponseError as e:
print(e)
try:
print("")
print("read data by id: application data fingerprint ...")
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT)
print(data.decode('utf-8'))
except NegativeResponseError as e:
print(e)

@ -0,0 +1,77 @@
#!/usr/bin/env python3
from tqdm import tqdm
from panda import Panda
from panda.python.uds import UdsClient, MessageTimeoutError, NegativeResponseError, SESSION_TYPE, DATA_IDENTIFIER_TYPE
if __name__ == "__main__":
addrs = [0x700 + i for i in range(256)]
addrs += [0x18da0000 + (i<<8) + 0xf1 for i in range(256)]
results = {}
panda = Panda()
panda.set_safety_mode(Panda.SAFETY_ELM327)
print("querying addresses ...")
with tqdm(addrs) as t:
for addr in t:
# skip functional broadcast addrs
if addr == 0x7df or addr == 0x18db33f1:
continue
t.set_description(hex(addr))
uds_client = UdsClient(panda, addr, bus=1 if panda.has_obd() else 0, timeout=0.1, debug=False)
try:
uds_client.tester_present()
uds_client.diagnostic_session_control(SESSION_TYPE.EXTENDED_DIAGNOSTIC)
except NegativeResponseError:
pass
except MessageTimeoutError:
continue
resp = {}
try:
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION)
if data: resp[DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION] = data
except NegativeResponseError:
pass
try:
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION)
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION] = data
except NegativeResponseError:
pass
try:
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION] = data
except NegativeResponseError:
pass
try:
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT)
if data: resp[DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT] = data
except NegativeResponseError:
pass
try:
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT)
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT] = data
except NegativeResponseError:
pass
try:
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT)
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT] = data
except NegativeResponseError:
pass
if resp.keys():
results[addr] = resp
print("results:")
if len(results.items()):
for addr, resp in results.items():
for rid, dat in resp.items():
print(hex(addr), hex(rid), dat.decode())
else:
print("no fw versions found!")

@ -22,7 +22,7 @@ def tesla_tester():
body_bus_num = 1 # My TDC to OBD adapter has PT on bus0 BDY on bus1 and CH on bus2 body_bus_num = 1 # My TDC to OBD adapter has PT on bus0 BDY on bus1 and CH on bus2
p.set_can_speed_kbps(body_bus_num, body_bus_speed) p.set_can_speed_kbps(body_bus_num, body_bus_speed)
# Now set the panda from its default of SAFETY_NOOUTPUT (read only) to SAFETY_ALLOUTPUT # Now set the panda from its default of SAFETY_SILENT (read only) to SAFETY_ALLOUTPUT
# Careful, as this will let us send any CAN messages we want (which could be very bad!) # Careful, as this will let us send any CAN messages we want (which could be very bad!)
print("Setting Panda to output mode...") print("Setting Panda to output mode...")
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
@ -37,7 +37,7 @@ def tesla_tester():
#Back to safety... #Back to safety...
print("Disabling output on Panda...") print("Disabling output on Panda...")
p.set_safety_mode(Panda.SAFETY_NOOUTPUT) p.set_safety_mode(Panda.SAFETY_SILENT)
print("Reading VIN from 0x568. This is painfully slow and can take up to 3 minutes (1 minute per message; 3 messages needed for full VIN)...") print("Reading VIN from 0x568. This is painfully slow and can take up to 3 minutes (1 minute per message; 3 messages needed for full VIN)...")

@ -110,7 +110,7 @@ class WifiHandle(object):
class Panda(object): class Panda(object):
# matches cereal.car.CarParams.SafetyModel # matches cereal.car.CarParams.SafetyModel
SAFETY_NOOUTPUT = 0 SAFETY_SILENT = 0
SAFETY_HONDA = 1 SAFETY_HONDA = 1
SAFETY_TOYOTA = 2 SAFETY_TOYOTA = 2
SAFETY_ELM327 = 3 SAFETY_ELM327 = 3
@ -127,6 +127,7 @@ class Panda(object):
SAFETY_TOYOTA_IPAS = 16 SAFETY_TOYOTA_IPAS = 16
SAFETY_ALLOUTPUT = 17 SAFETY_ALLOUTPUT = 17
SAFETY_GM_ASCM = 18 SAFETY_GM_ASCM = 18
SAFETY_NOOUTPUT = 19
SERIAL_DEBUG = 0 SERIAL_DEBUG = 0
SERIAL_ESP = 1 SERIAL_ESP = 1
@ -345,18 +346,25 @@ class Panda(object):
# ******************* health ******************* # ******************* health *******************
def health(self): def health(self):
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 24) dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 37)
a = struct.unpack("IIIIIBBBB", dat) a = struct.unpack("IIIIIIIBBBBBBBBB", dat)
return { return {
"voltage": a[0], "uptime": a[0],
"current": a[1], "voltage": a[1],
"can_send_errs": a[2], "current": a[2],
"can_fwd_errs": a[3], "can_send_errs": a[3],
"gmlan_send_errs": a[4], "can_fwd_errs": a[4],
"started": a[5], "gmlan_send_errs": a[5],
"controls_allowed": a[6], "faults": a[6],
"gas_interceptor_detected": a[7], "ignition_line": a[7],
"car_harness_status": a[8] "ignition_can": a[8],
"controls_allowed": a[9],
"gas_interceptor_detected": a[10],
"car_harness_status": a[11],
"usb_power_mode": a[12],
"safety_mode": a[13],
"fault_status": a[14],
"power_save_enabled": a[15]
} }
# ******************* control ******************* # ******************* control *******************
@ -371,6 +379,17 @@ class Panda(object):
def get_version(self): def get_version(self):
return self._handle.controlRead(Panda.REQUEST_IN, 0xd6, 0, 0, 0x40).decode('utf8') return self._handle.controlRead(Panda.REQUEST_IN, 0xd6, 0, 0, 0x40).decode('utf8')
@staticmethod
def get_signature_from_firmware(fn):
f = open(fn, 'rb')
f.seek(-128, 2) # Seek from end of file
return f.read(128)
def get_signature(self):
part_1 = self._handle.controlRead(Panda.REQUEST_IN, 0xd3, 0, 0, 0x40)
part_2 = self._handle.controlRead(Panda.REQUEST_IN, 0xd4, 0, 0, 0x40)
return part_1 + part_2
def get_type(self): def get_type(self):
return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40) return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40)
@ -386,6 +405,9 @@ class Panda(object):
def is_uno(self): def is_uno(self):
return self.get_type() == Panda.HW_TYPE_UNO return self.get_type() == Panda.HW_TYPE_UNO
def has_obd(self):
return (self.is_uno() or self.is_black())
def get_serial(self): def get_serial(self):
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20) dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20)
hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4] hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4]
@ -400,6 +422,9 @@ class Panda(object):
def set_usb_power(self, on): def set_usb_power(self, on):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe6, int(on), 0, b'') self._handle.controlWrite(Panda.REQUEST_OUT, 0xe6, int(on), 0, b'')
def set_power_save(self, power_save_enabled=0):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe7, int(power_save_enabled), 0, b'')
def set_esp_power(self, on): def set_esp_power(self, on):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'') self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'')
@ -407,7 +432,7 @@ class Panda(object):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xda, int(bootmode), 0, b'') self._handle.controlWrite(Panda.REQUEST_OUT, 0xda, int(bootmode), 0, b'')
time.sleep(0.2) time.sleep(0.2)
def set_safety_mode(self, mode=SAFETY_NOOUTPUT): def set_safety_mode(self, mode=SAFETY_SILENT):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, 0, b'') self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, 0, b'')
def set_can_forwarding(self, from_bus, to_bus): def set_can_forwarding(self, from_bus, to_bus):
@ -623,4 +648,8 @@ class Panda(object):
def get_fan_rpm(self): def get_fan_rpm(self):
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xb2, 0, 0, 2) dat = self._handle.controlRead(Panda.REQUEST_IN, 0xb2, 0, 0, 2)
a = struct.unpack("H", dat) a = struct.unpack("H", dat)
return a[0] return a[0]
# ****************** Phone *****************
def set_phone_power(self, enabled):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xb3, int(enabled), 0, b'')

@ -1,11 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import time import time
import struct import struct
from typing import NamedTuple, List from typing import Callable, NamedTuple, Tuple, List
from enum import IntEnum from enum import IntEnum
from queue import Queue, Empty
from threading import Thread
from binascii import hexlify
class SERVICE_TYPE(IntEnum): class SERVICE_TYPE(IntEnum):
DIAGNOSTIC_SESSION_CONTROL = 0x10 DIAGNOSTIC_SESSION_CONTROL = 0x10
@ -213,6 +210,7 @@ class MessageTimeoutError(Exception):
class NegativeResponseError(Exception): class NegativeResponseError(Exception):
def __init__(self, message, service_id, error_code): def __init__(self, message, service_id, error_code):
super().__init__()
self.message = message self.message = message
self.service_id = service_id self.service_id = service_id
self.error_code = error_code self.error_code = error_code
@ -270,20 +268,79 @@ _negative_response_codes = {
0x93: 'voltage too low', 0x93: 'voltage too low',
} }
class CanClient():
def __init__(self, can_send: Callable[[Tuple[int, bytes, int]], None], can_recv: Callable[[], List[Tuple[int, int, bytes, int]]], tx_addr: int, rx_addr: int, bus: int, debug: bool=False):
self.tx = can_send
self.rx = can_recv
self.tx_addr = tx_addr
self.rx_addr = rx_addr
self.bus = bus
self.debug = debug
def _recv_filter(self, bus, addr):
# handle functionl addresses (switch to first addr to respond)
if self.tx_addr == 0x7DF:
is_response = addr >= 0x7E8 and addr <= 0x7EF
if is_response:
if self.debug: print(f"switch to physical addr {hex(addr)}")
self.tx_addr = addr-8
self.rx_addr = addr
return is_response
if self.tx_addr == 0x18DB33F1:
is_response = addr >= 0x18DAF100 and addr <= 0x18DAF1FF
if is_response:
if self.debug: print(f"switch to physical addr {hex(addr)}")
self.tx_addr = 0x18DA00F1 + (addr<<8 & 0xFF00)
self.rx_addr = addr
return bus == self.bus and addr == self.rx_addr
def recv(self, drain=False) -> List[bytes]:
msg_array = []
while True:
msgs = self.rx()
if drain:
if self.debug: print("CAN-RX: drain - {}".format(len(msgs)))
else:
for rx_addr, rx_ts, rx_data, rx_bus in msgs or []:
if self._recv_filter(rx_bus, rx_addr) and len(rx_data) > 0:
rx_data = bytes(rx_data) # convert bytearray to bytes
if self.debug: print(f"CAN-RX: {hex(rx_addr)} - 0x{bytes.hex(rx_data)}")
msg_array.append(rx_data)
# break when non-full buffer is processed
if len(msgs) < 254:
return msg_array
def send(self, msgs: List[bytes], delay: float=0) -> None:
first = True
for msg in msgs:
if delay and not first:
if self.debug: print(f"CAN-TX: delay - {delay}")
time.sleep(delay)
if self.debug: print(f"CAN-TX: {hex(self.tx_addr)} - 0x{bytes.hex(msg)}")
self.tx(self.tx_addr, msg, self.bus)
first = False
class IsoTpMessage(): class IsoTpMessage():
def __init__(self, can_tx_queue: Queue, can_rx_queue: Queue, timeout: float, debug: bool=False): def __init__(self, can_client: CanClient, timeout: float=1, debug: bool=False):
self.can_tx_queue = can_tx_queue self._can_client = can_client
self.can_rx_queue = can_rx_queue
self.timeout = timeout self.timeout = timeout
self.debug = debug self.debug = debug
def send(self, dat: bytes) -> None: def send(self, dat: bytes) -> None:
# throw away any stale data
self._can_client.recv(drain=True)
self.tx_dat = dat self.tx_dat = dat
self.tx_len = len(dat) self.tx_len = len(dat)
self.tx_idx = 0 self.tx_idx = 0
self.tx_done = False self.tx_done = False
if self.debug: print(f"ISO-TP: REQUEST - {hexlify(self.tx_dat)}") self.rx_dat = b""
self.rx_len = 0
self.rx_idx = 0
self.rx_done = False
if self.debug: print(f"ISO-TP: REQUEST - 0x{bytes.hex(self.tx_dat)}")
self._tx_first_frame() self._tx_first_frame()
def _tx_first_frame(self) -> None: def _tx_first_frame(self) -> None:
@ -296,27 +353,25 @@ class IsoTpMessage():
# first frame (send first 6 bytes) # first frame (send first 6 bytes)
if self.debug: print("ISO-TP: TX - first frame") if self.debug: print("ISO-TP: TX - first frame")
msg = (struct.pack("!H", 0x1000 | self.tx_len) + self.tx_dat[:6]).ljust(8, b"\x00") msg = (struct.pack("!H", 0x1000 | self.tx_len) + self.tx_dat[:6]).ljust(8, b"\x00")
self.can_tx_queue.put(msg) self._can_client.send([msg])
def recv(self) -> bytes: def recv(self) -> bytes:
self.rx_dat = b"" start_time = time.time()
self.rx_len = 0
self.rx_idx = 0
self.rx_done = False
try: try:
while True: while True:
self._isotp_rx_next() for msg in self._can_client.recv():
if self.tx_done and self.rx_done: self._isotp_rx_next(msg)
return self.rx_dat if self.tx_done and self.rx_done:
except Empty: return self.rx_dat
raise MessageTimeoutError("timeout waiting for response") # no timeout indicates non-blocking
if self.timeout == 0:
return None
if time.time() - start_time > self.timeout:
raise MessageTimeoutError("timeout waiting for response")
finally: finally:
if self.debug: print(f"ISO-TP: RESPONSE - {hexlify(self.rx_dat)}") if self.debug and self.rx_dat: print(f"ISO-TP: RESPONSE - 0x{bytes.hex(self.rx_dat)}")
def _isotp_rx_next(self) -> None:
rx_data = self.can_rx_queue.get(block=True, timeout=self.timeout)
def _isotp_rx_next(self, rx_data: bytes) -> None:
# single rx_frame # single rx_frame
if rx_data[0] >> 4 == 0x0: if rx_data[0] >> 4 == 0x0:
self.rx_len = rx_data[0] & 0xFF self.rx_len = rx_data[0] & 0xFF
@ -336,9 +391,9 @@ class IsoTpMessage():
if self.debug: print(f"ISO-TP: TX - flow control continue") if self.debug: print(f"ISO-TP: TX - flow control continue")
# send flow control message (send all bytes) # send flow control message (send all bytes)
msg = b"\x30\x00\x00".ljust(8, b"\x00") msg = b"\x30\x00\x00".ljust(8, b"\x00")
self.can_tx_queue.put(msg) self._can_client.send([msg])
return return
# consecutive rx frame # consecutive rx frame
if rx_data[0] >> 4 == 0x2: if rx_data[0] >> 4 == 0x2:
assert self.rx_done == False, "isotp - rx: consecutive frame with no active frame" assert self.rx_done == False, "isotp - rx: consecutive frame with no active frame"
@ -361,19 +416,19 @@ class IsoTpMessage():
delay_ts = rx_data[2] & 0x7F delay_ts = rx_data[2] & 0x7F
# scale is 1 milliseconds if first bit == 0, 100 micro seconds if first bit == 1 # scale is 1 milliseconds if first bit == 0, 100 micro seconds if first bit == 1
delay_div = 1000. if rx_data[2] & 0x80 == 0 else 10000. delay_div = 1000. if rx_data[2] & 0x80 == 0 else 10000.
delay_sec = delay_ts / delay_div
# first frame = 6 bytes, each consecutive frame = 7 bytes # first frame = 6 bytes, each consecutive frame = 7 bytes
start = 6 + self.tx_idx * 7 start = 6 + self.tx_idx * 7
count = rx_data[1] count = rx_data[1]
end = start + count * 7 if count > 0 else self.tx_len end = start + count * 7 if count > 0 else self.tx_len
tx_msgs = []
for i in range(start, end, 7): for i in range(start, end, 7):
if delay_ts > 0 and i > start:
delay_s = delay_ts / delay_div
if self.debug: print(f"ISO-TP: TX - delay - seconds={delay_s}")
time.sleep(delay_s)
self.tx_idx += 1 self.tx_idx += 1
# consecutive tx frames # consecutive tx messages
msg = (bytes([0x20 | (self.tx_idx & 0xF)]) + self.tx_dat[i:i+7]).ljust(8, b"\x00") msg = (bytes([0x20 | (self.tx_idx & 0xF)]) + self.tx_dat[i:i+7]).ljust(8, b"\x00")
self.can_tx_queue.put(msg) tx_msgs.append(msg)
# send consecutive tx messages
self._can_client.send(tx_msgs, delay=delay_sec)
if end >= self.tx_len: if end >= self.tx_len:
self.tx_done = True self.tx_done = True
if self.debug: print(f"ISO-TP: TX - consecutive frame - idx={self.tx_idx} done={self.tx_done}") if self.debug: print(f"ISO-TP: TX - consecutive frame - idx={self.tx_idx} done={self.tx_done}")
@ -381,57 +436,30 @@ class IsoTpMessage():
# wait (do nothing until next flow control message) # wait (do nothing until next flow control message)
if self.debug: print("ISO-TP: TX - flow control wait") if self.debug: print("ISO-TP: TX - flow control wait")
FUNCTIONAL_ADDRS = [0x7DF, 0x18DB33F1]
def get_rx_addr_for_tx_addr(tx_addr):
if tx_addr in FUNCTIONAL_ADDRS:
return None
if tx_addr < 0xFFF8:
# standard 11 bit response addr (add 8)
return tx_addr + 8
if tx_addr > 0x10000000 and tx_addr < 0xFFFFFFFF:
# standard 29 bit response addr (flip last two bytes)
return (tx_addr & 0xFFFF0000) + (tx_addr<<8 & 0xFF00) + (tx_addr>>8 & 0xFF)
raise ValueError("invalid tx_addr: {}".format(tx_addr))
class UdsClient(): class UdsClient():
def __init__(self, panda, tx_addr: int, rx_addr: int=None, bus: int=0, timeout: float=10, debug: bool=False): def __init__(self, panda, tx_addr: int, rx_addr: int=None, bus: int=0, timeout: float=1, debug: bool=False):
self.panda = panda
self.bus = bus self.bus = bus
self.tx_addr = tx_addr self.tx_addr = tx_addr
if rx_addr == None: self.rx_addr = rx_addr if rx_addr is not None else get_rx_addr_for_tx_addr(tx_addr)
if tx_addr < 0xFFF8:
# standard 11 bit response addr (add 8)
self.rx_addr = tx_addr+8
elif tx_addr > 0x10000000 and tx_addr < 0xFFFFFFFF:
# standard 29 bit response addr (flip last two bytes)
self.rx_addr = (tx_addr & 0xFFFF0000) + (tx_addr<<8 & 0xFF00) + (tx_addr>>8 & 0xFF)
else:
raise ValueError("invalid tx_addr: {}".format(tx_addr))
self.can_tx_queue = Queue()
self.can_rx_queue = Queue()
self.timeout = timeout self.timeout = timeout
self.debug = debug self.debug = debug
self._can_client = CanClient(panda.can_send, panda.can_recv, self.tx_addr, self.rx_addr, self.bus, debug=self.debug)
self.can_thread = Thread(target=self._can_thread, args=(self.debug,))
self.can_thread.daemon = True
self.can_thread.start()
def _can_thread(self, debug: bool=False):
try:
# allow all output
self.panda.set_safety_mode(0x1337)
# clear tx buffer
self.panda.can_clear(self.bus)
# clear rx buffer
self.panda.can_clear(0xFFFF)
while True:
# send
while not self.can_tx_queue.empty():
msg = self.can_tx_queue.get(block=False)
if debug: print("CAN-TX: {} - {}".format(hex(self.tx_addr), hexlify(msg)))
self.panda.can_send(self.tx_addr, msg, self.bus)
# receive
msgs = self.panda.can_recv()
for rx_addr, rx_ts, rx_data, rx_bus in msgs:
if rx_bus != self.bus or rx_addr != self.rx_addr or len(rx_data) == 0:
continue
if debug: print("CAN-RX: {} - {}".format(hex(self.rx_addr), hexlify(rx_data)))
self.can_rx_queue.put(rx_data)
else:
time.sleep(0.01)
finally:
self.panda.close()
# generic uds request # generic uds request
def _uds_request(self, service_type: SERVICE_TYPE, subfunction: int=None, data: bytes=None) -> bytes: def _uds_request(self, service_type: SERVICE_TYPE, subfunction: int=None, data: bytes=None) -> bytes:
@ -442,7 +470,7 @@ class UdsClient():
req += data req += data
# send request, wait for response # send request, wait for response
isotp_msg = IsoTpMessage(self.can_tx_queue, self.can_rx_queue, self.timeout, self.debug) isotp_msg = IsoTpMessage(self._can_client, self.timeout, self.debug)
isotp_msg.send(req) isotp_msg.send(req)
while True: while True:
resp = isotp_msg.recv() resp = isotp_msg.recv()
@ -453,12 +481,12 @@ class UdsClient():
service_id = resp[1] if len(resp) > 1 else -1 service_id = resp[1] if len(resp) > 1 else -1
try: try:
service_desc = SERVICE_TYPE(service_id).name service_desc = SERVICE_TYPE(service_id).name
except Exception: except BaseException:
service_desc = 'NON_STANDARD_SERVICE' service_desc = 'NON_STANDARD_SERVICE'
error_code = resp[2] if len(resp) > 2 else -1 error_code = resp[2] if len(resp) > 2 else -1
try: try:
error_desc = _negative_response_codes[error_code] error_desc = _negative_response_codes[error_code]
except Exception: except BaseException:
error_desc = resp[3:] error_desc = resp[3:]
# wait for another message if response pending # wait for another message if response pending
if error_code == 0x78: if error_code == 0x78:

@ -6,6 +6,6 @@ tqdm>=4.14.0
nose nose
parameterized parameterized
requests requests
flake8==3.7.8 flake8==3.7.9
pylint==2.4.2 pylint==2.4.3
cffi==1.11.4 cffi==1.11.4

@ -1,11 +1,34 @@
from .helpers import test_all_pandas, panda_connect_and_init import os
from nose.tools import assert_equal
from panda import Panda, BASEDIR
from .helpers import reset_pandas, test_all_pandas, panda_connect_and_init
# Reset the pandas before flashing them
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas @test_all_pandas
@panda_connect_and_init @panda_connect_and_init
def test_recover(p): def test_recover(p):
assert p.recover(timeout=30) assert p.recover(timeout=30)
@test_all_pandas @test_all_pandas
@panda_connect_and_init @panda_connect_and_init
def test_flash(p): def test_flash(p):
p.flash() p.flash()
@test_all_pandas
@panda_connect_and_init
def test_get_signature(p):
fn = os.path.join(BASEDIR, "board/obj/panda.bin")
firmware_sig = Panda.get_signature_from_firmware(fn)
panda_sig = p.get_signature()
assert_equal(panda_sig, firmware_sig)

@ -0,0 +1,44 @@
import time
from panda_jungle import PandaJungle # pylint: disable=import-error
from .helpers import panda_jungle, reset_pandas, test_all_pandas, test_all_gen2_pandas, panda_connect_and_init
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas
@panda_connect_and_init
def test_ignition(p):
try:
# Set harness orientation to #2, since the ignition line is on the wrong SBU bus :/
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_2)
reset_pandas()
p.reconnect()
panda_jungle.set_ignition(False)
time.sleep(2)
assert p.health()['ignition_line'] == False
panda_jungle.set_ignition(True)
time.sleep(2)
assert p.health()['ignition_line'] == True
finally:
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1)
@test_all_gen2_pandas
@panda_connect_and_init
def test_orientation_detection(p):
seen_orientations = []
for i in range(3):
panda_jungle.set_harness_orientation(i)
reset_pandas()
p.reconnect()
detected_harness_orientation = p.health()['car_harness_status']
if (i == 0 and detected_harness_orientation != 0) or detected_harness_orientation in seen_orientations:
assert False
seen_orientations.append(detected_harness_orientation)
@test_all_pandas
@panda_connect_and_init
def test_voltage(p):
voltage = p.health()['voltage']
assert ((voltage > 10000) and (voltage < 14000))

@ -2,11 +2,18 @@ import sys
import time import time
from panda import Panda from panda import Panda
from nose.tools import assert_equal, assert_less, assert_greater from nose.tools import assert_equal, assert_less, assert_greater
from .helpers import SPEED_NORMAL, SPEED_GMLAN, time_many_sends, test_white_and_grey, panda_type_to_serial, test_all_pandas, panda_connect_and_init from .helpers import start_heartbeat_thread, reset_pandas, SPEED_NORMAL, SPEED_GMLAN, time_many_sends, test_white_and_grey, panda_type_to_serial, test_all_pandas, panda_connect_and_init
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas @test_all_pandas
@panda_connect_and_init @panda_connect_and_init
def test_can_loopback(p): def test_can_loopback(p):
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
@ -40,8 +47,11 @@ def test_can_loopback(p):
@test_all_pandas @test_all_pandas
@panda_connect_and_init @panda_connect_and_init
def test_safety_nooutput(p): def test_safety_nooutput(p):
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_NOOUTPUT) p.set_safety_mode(Panda.SAFETY_SILENT)
# enable CAN loopback mode # enable CAN loopback mode
p.set_can_loopback(True) p.set_can_loopback(True)
@ -60,6 +70,9 @@ def test_reliability(p):
LOOP_COUNT = 100 LOOP_COUNT = 100
MSG_COUNT = 100 MSG_COUNT = 100
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_can_loopback(True) p.set_can_loopback(True)
@ -95,6 +108,9 @@ def test_reliability(p):
@test_all_pandas @test_all_pandas
@panda_connect_and_init @panda_connect_and_init
def test_throughput(p): def test_throughput(p):
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
@ -122,6 +138,9 @@ def test_gmlan(p):
if p.legacy: if p.legacy:
return return
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
@ -153,6 +172,9 @@ def test_gmlan_bad_toggle(p):
if p.legacy: if p.legacy:
return return
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)

@ -1,8 +1,12 @@
import time import time
from panda import Panda from panda import Panda
from .helpers import connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init from .helpers import reset_pandas, connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init
import requests import requests
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas @test_all_pandas
@panda_connect_and_init @panda_connect_and_init
def test_get_serial(p): def test_get_serial(p):

@ -1,6 +1,10 @@
import time import time
from panda import Panda from panda import Panda
from .helpers import time_many_sends, connect_wifi, test_white, panda_type_to_serial from .helpers import start_heartbeat_thread, reset_pandas, time_many_sends, connect_wifi, test_white, panda_type_to_serial
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_white @test_white
@panda_type_to_serial @panda_type_to_serial
@ -16,6 +20,9 @@ def test_throughput(serials=None):
connect_wifi(serials[0]) connect_wifi(serials[0])
p = Panda(serials[0]) p = Panda(serials[0])
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
@ -43,6 +50,10 @@ def test_throughput(serials=None):
def test_recv_only(serials=None): def test_recv_only(serials=None):
connect_wifi(serials[0]) connect_wifi(serials[0])
p = Panda(serials[0]) p = Panda(serials[0])
# Start heartbeat
start_heartbeat_thread(p)
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_can_loopback(True) p.set_can_loopback(True)

@ -1,195 +0,0 @@
import os
import time
import random
from panda import Panda
from nose.tools import assert_equal, assert_less, assert_greater
from .helpers import time_many_sends, test_two_panda, test_two_black_panda, panda_type_to_serial, clear_can_buffers, panda_connect_and_init
@test_two_panda
@panda_type_to_serial
@panda_connect_and_init
def test_send_recv(p_send, p_recv):
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_send.set_can_loopback(False)
p_recv.set_can_loopback(False)
assert not p_send.legacy
assert not p_recv.legacy
p_send.can_send_many([(0x1ba, 0, b"message", 0)]*2)
time.sleep(0.05)
p_recv.can_recv()
p_send.can_recv()
busses = [0,1,2]
for bus in busses:
for speed in [100, 250, 500, 750, 1000]:
p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.05)
comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True)
saturation_pct = (comp_kbps/speed) * 100.0
assert_greater(saturation_pct, 80)
assert_less(saturation_pct, 100)
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct))
@test_two_panda
@panda_type_to_serial
@panda_connect_and_init
def test_latency(p_send, p_recv):
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_send.set_can_loopback(False)
p_recv.set_can_loopback(False)
assert not p_send.legacy
assert not p_recv.legacy
p_send.set_can_speed_kbps(0, 100)
p_recv.set_can_speed_kbps(0, 100)
time.sleep(0.05)
p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10)
time.sleep(0.05)
p_recv.can_recv()
p_send.can_recv()
busses = [0,1,2]
for bus in busses:
for speed in [100, 250, 500, 750, 1000]:
p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.1)
#clear can buffers
clear_can_buffers(p_send)
clear_can_buffers(p_recv)
latencies = []
comp_kbps_list = []
saturation_pcts = []
num_messages = 100
for i in range(num_messages):
st = time.time()
p_send.can_send(0x1ab, b"message", bus)
r = []
while len(r) < 1 and (time.time() - st) < 5:
r = p_recv.can_recv()
et = time.time()
r_echo = []
while len(r_echo) < 1 and (time.time() - st) < 10:
r_echo = p_send.can_recv()
if len(r) == 0 or len(r_echo) == 0:
print("r: {}, r_echo: {}".format(r, r_echo))
assert_equal(len(r),1)
assert_equal(len(r_echo),1)
et = (et - st)*1000.0
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et
latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed)
assert_less(latency, 5.0)
saturation_pct = (comp_kbps/speed) * 100.0
latencies.append(latency)
comp_kbps_list.append(comp_kbps)
saturation_pcts.append(saturation_pct)
average_latency = sum(latencies)/num_messages
assert_less(average_latency, 1.0)
average_comp_kbps = sum(comp_kbps_list)/num_messages
average_saturation_pct = sum(saturation_pcts)/num_messages
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct))
@test_two_black_panda
@panda_type_to_serial
@panda_connect_and_init
def test_black_loopback(panda0, panda1):
# disable safety modes
panda0.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
panda1.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# disable loopback
panda0.set_can_loopback(False)
panda1.set_can_loopback(False)
# clear stuff
panda0.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10)
time.sleep(0.05)
panda0.can_recv()
panda1.can_recv()
# test array (send bus, sender obd, reciever obd, expected busses)
test_array = [
(0, False, False, [0]),
(1, False, False, [1]),
(2, False, False, [2]),
(0, False, True, [0, 1]),
(1, False, True, []),
(2, False, True, [2]),
(0, True, False, [0]),
(1, True, False, [0]),
(2, True, False, [2]),
(0, True, True, [0, 1]),
(1, True, True, [0, 1]),
(2, True, True, [2])
]
# test functions
def get_test_string():
return b"test"+os.urandom(10)
def _test_buses(send_panda, recv_panda, _test_array):
for send_bus, send_obd, recv_obd, recv_buses in _test_array:
print("\nSend bus:", send_bus, " Send OBD:", send_obd, " Recv OBD:", recv_obd)
# set OBD on pandas
send_panda.set_gmlan(True if send_obd else None)
recv_panda.set_gmlan(True if recv_obd else None)
# clear buffers
clear_can_buffers(send_panda)
clear_can_buffers(recv_panda)
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
send_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
_ = send_panda.can_recv() # cans echo
cans_loop = recv_panda.can_recv()
loop_buses = []
for loop in cans_loop:
print(" Loop on bus", str(loop[3]))
loop_buses.append(loop[3])
if len(cans_loop) == 0:
print(" No loop")
# test loop buses
recv_buses.sort()
loop_buses.sort()
assert recv_buses == loop_buses
print(" TEST PASSED")
print("\n")
# test both orientations
print("***************** TESTING (0 --> 1) *****************")
_test_buses(panda0, panda1, test_array)
print("***************** TESTING (1 --> 0) *****************")
_test_buses(panda1, panda0, test_array)

@ -1,16 +1,23 @@
import sys import sys
import time import time
from .helpers import time_many_sends, connect_wifi, test_white, panda_type_to_serial from .helpers import start_heartbeat_thread, reset_pandas, time_many_sends, connect_wifi, test_white, panda_type_to_serial
from panda import Panda, PandaWifiStreaming from panda import Panda, PandaWifiStreaming
from nose.tools import assert_less, assert_greater from nose.tools import assert_less, assert_greater
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_white @test_white
@panda_type_to_serial @panda_type_to_serial
def test_udp_doesnt_drop(serials=None): def test_udp_doesnt_drop(serials=None):
connect_wifi(serials[0]) connect_wifi(serials[0])
p = Panda(serials[0]) p = Panda(serials[0])
# Start heartbeat
start_heartbeat_thread(p)
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_can_loopback(True) p.set_can_loopback(True)

@ -0,0 +1,202 @@
import os
import time
import random
from panda import Panda
from nose.tools import assert_equal, assert_less, assert_greater
from .helpers import panda_jungle, start_heartbeat_thread, reset_pandas, time_many_sends, test_all_pandas, test_all_gen2_pandas, clear_can_buffers, panda_connect_and_init
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas
@panda_connect_and_init
def test_send_recv(p):
def test(p_send, p_recv):
p_send.set_can_loopback(False)
p_recv.set_can_loopback(False)
p_send.can_send_many([(0x1ba, 0, b"message", 0)]*2)
time.sleep(0.05)
p_recv.can_recv()
p_send.can_recv()
busses = [0,1,2]
for bus in busses:
for speed in [100, 250, 500, 750, 1000]:
p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.05)
clear_can_buffers(p_send)
clear_can_buffers(p_recv)
comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True)
saturation_pct = (comp_kbps/speed) * 100.0
assert_greater(saturation_pct, 80)
assert_less(saturation_pct, 100)
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct))
# Start heartbeat
start_heartbeat_thread(p)
# Set safety mode and power saving
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
try:
# Run tests in both directions
test(p, panda_jungle)
test(panda_jungle, p)
except Exception as e:
# Raise errors again, we don't want them to get lost
raise e
finally:
# Set back to silent mode
p.set_safety_mode(Panda.SAFETY_SILENT)
@test_all_pandas
@panda_connect_and_init
def test_latency(p):
def test(p_send, p_recv):
p_send.set_can_loopback(False)
p_recv.set_can_loopback(False)
p_send.set_can_speed_kbps(0, 100)
p_recv.set_can_speed_kbps(0, 100)
time.sleep(0.05)
p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10)
time.sleep(0.05)
p_recv.can_recv()
p_send.can_recv()
busses = [0,1,2]
for bus in busses:
for speed in [100, 250, 500, 750, 1000]:
p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.1)
# clear can buffers
clear_can_buffers(p_send)
clear_can_buffers(p_recv)
latencies = []
comp_kbps_list = []
saturation_pcts = []
num_messages = 100
for i in range(num_messages):
st = time.time()
p_send.can_send(0x1ab, b"message", bus)
r = []
while len(r) < 1 and (time.time() - st) < 5:
r = p_recv.can_recv()
et = time.time()
r_echo = []
while len(r_echo) < 1 and (time.time() - st) < 10:
r_echo = p_send.can_recv()
if len(r) == 0 or len(r_echo) == 0:
print("r: {}, r_echo: {}".format(r, r_echo))
assert_equal(len(r),1)
assert_equal(len(r_echo),1)
et = (et - st)*1000.0
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et
latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed)
assert_less(latency, 5.0)
saturation_pct = (comp_kbps/speed) * 100.0
latencies.append(latency)
comp_kbps_list.append(comp_kbps)
saturation_pcts.append(saturation_pct)
average_latency = sum(latencies)/num_messages
assert_less(average_latency, 1.0)
average_comp_kbps = sum(comp_kbps_list)/num_messages
average_saturation_pct = sum(saturation_pcts)/num_messages
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct))
# Start heartbeat
start_heartbeat_thread(p)
# Set safety mode and power saving
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
try:
# Run tests in both directions
test(p, panda_jungle)
test(panda_jungle, p)
except Exception as e:
# Raise errors again, we don't want them to get lost
raise e
finally:
# Set back to silent mode
p.set_safety_mode(Panda.SAFETY_SILENT)
@test_all_gen2_pandas
@panda_connect_and_init
def test_gen2_loopback(p):
def test(p_send, p_recv):
for bus in range(4):
obd = False
if bus == 3:
obd = True
bus = 1
# Clear buses
clear_can_buffers(p_send)
clear_can_buffers(p_recv)
# Send a random string
addr = random.randint(1, 2000)
string = b"test"+os.urandom(4)
p_send.set_obd(obd)
p_recv.set_obd(obd)
time.sleep(0.2)
p_send.can_send(addr, string, bus)
time.sleep(0.2)
content = p_recv.can_recv()
# Check amount of messages
assert len(content) == 1
# Check content
assert content[0][0] == addr and content[0][2] == string
# Check bus
assert content[0][3] == bus
print("Bus:", bus, "OBD:", obd, "OK")
# Start heartbeat
start_heartbeat_thread(p)
# Set safety mode and power saving
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
try:
# Run tests in both directions
test(p, panda_jungle)
test(panda_jungle, p)
except Exception as e:
# Raise errors again, we don't want them to get lost
raise e
finally:
# Set back to silent mode
p.set_safety_mode(Panda.SAFETY_SILENT)

@ -5,21 +5,52 @@ import random
import subprocess import subprocess
import requests import requests
import _thread import _thread
import faulthandler
from functools import wraps from functools import wraps
from panda import Panda from panda import Panda
from panda_jungle import PandaJungle # pylint: disable=import-error
from nose.tools import assert_equal from nose.tools import assert_equal
from parameterized import parameterized, param from parameterized import parameterized, param
from .timeout import run_with_timeout
from .wifi_helpers import _connect_wifi
SPEED_NORMAL = 500 SPEED_NORMAL = 500
SPEED_GMLAN = 33.3 SPEED_GMLAN = 33.3
BUS_SPEEDS = [(0, SPEED_NORMAL), (1, SPEED_NORMAL), (2, SPEED_NORMAL), (3, SPEED_GMLAN)]
TIMEOUT = 30
GEN2_HW_TYPES = [Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_UNO]
# Enable fault debug
faulthandler.enable(all_threads=False)
# Connect to Panda Jungle
panda_jungle = PandaJungle()
# Find all panda's connected
_panda_serials = None
def init_panda_serials():
global panda_jungle, _panda_serials
_panda_serials = []
panda_jungle.set_panda_power(True)
time.sleep(5)
for serial in Panda.list():
p = Panda(serial=serial)
_panda_serials.append((serial, p.get_type()))
p.close()
print('Found', str(len(_panda_serials)), 'pandas')
init_panda_serials()
# Panda providers
test_all_types = parameterized([ test_all_types = parameterized([
param(panda_type=Panda.HW_TYPE_WHITE_PANDA), param(panda_type=Panda.HW_TYPE_WHITE_PANDA),
param(panda_type=Panda.HW_TYPE_GREY_PANDA), param(panda_type=Panda.HW_TYPE_GREY_PANDA),
param(panda_type=Panda.HW_TYPE_BLACK_PANDA) param(panda_type=Panda.HW_TYPE_BLACK_PANDA)
]) ])
test_all_pandas = parameterized( test_all_pandas = parameterized(
Panda.list() list(map(lambda x: x[0], _panda_serials))
)
test_all_gen2_pandas = parameterized(
list(map(lambda x: x[0], filter(lambda x: x[1] in GEN2_HW_TYPES, _panda_serials)))
) )
test_white_and_grey = parameterized([ test_white_and_grey = parameterized([
param(panda_type=Panda.HW_TYPE_WHITE_PANDA), param(panda_type=Panda.HW_TYPE_WHITE_PANDA),
@ -31,13 +62,8 @@ test_white = parameterized([
test_grey = parameterized([ test_grey = parameterized([
param(panda_type=Panda.HW_TYPE_GREY_PANDA) param(panda_type=Panda.HW_TYPE_GREY_PANDA)
]) ])
test_two_panda = parameterized([ test_black = parameterized([
param(panda_type=[Panda.HW_TYPE_GREY_PANDA, Panda.HW_TYPE_WHITE_PANDA]), param(panda_type=Panda.HW_TYPE_BLACK_PANDA)
param(panda_type=[Panda.HW_TYPE_WHITE_PANDA, Panda.HW_TYPE_GREY_PANDA]),
param(panda_type=[Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_BLACK_PANDA])
])
test_two_black_panda = parameterized([
param(panda_type=[Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_BLACK_PANDA])
]) ])
def connect_wifi(serial=None): def connect_wifi(serial=None):
@ -47,111 +73,26 @@ def connect_wifi(serial=None):
assert(dongle_id.isalnum()) assert(dongle_id.isalnum())
_connect_wifi(dongle_id, pw) _connect_wifi(dongle_id, pw)
FNULL = open(os.devnull, 'w') def time_many_sends(p, bus, p_recv=None, msg_count=100, msg_id=None, two_pandas=False):
def _connect_wifi(dongle_id, pw, insecure_okay=False): if p_recv == None:
ssid = "panda-" + dongle_id p_recv = p
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if not r:
#Can already ping, try connecting on wifi
try:
p = Panda("WIFI")
p.get_serial()
print("Already connected")
return
except:
pass
print("WIFI: connecting to %s" % ssid)
while 1:
if sys.platform == "darwin":
os.system("networksetup -setairportnetwork en0 %s %s" % (ssid, pw))
else:
wlan_interface = subprocess.check_output(["sh", "-c", "iw dev | awk '/Interface/ {print $2}'"]).strip().decode('utf8')
cnt = 0
MAX_TRIES = 10
while cnt < MAX_TRIES:
print("WIFI: scanning %d" % cnt)
os.system("iwlist %s scanning > /dev/null" % wlan_interface)
os.system("nmcli device wifi rescan")
wifi_networks = [x.decode("utf8") for x in subprocess.check_output(["nmcli","dev", "wifi", "list"]).split(b"\n")]
wifi_scan = [x for x in wifi_networks if ssid in x]
if len(wifi_scan) != 0:
break
time.sleep(0.1)
# MAX_TRIES tries, ~10 seconds max
cnt += 1
assert cnt < MAX_TRIES
if "-pair" in wifi_scan[0]:
os.system("nmcli d wifi connect %s-pair" % (ssid))
connect_cnt = 0
MAX_TRIES = 100
while connect_cnt < MAX_TRIES:
connect_cnt += 1
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if r:
print("Waiting for panda to ping...")
time.sleep(0.5)
else:
break
if insecure_okay:
break
# fetch webpage
print("connecting to insecure network to secure")
try:
r = requests.get("http://192.168.0.10/")
except requests.ConnectionError:
r = requests.get("http://192.168.0.10/")
assert r.status_code==200
print("securing")
try:
r = requests.get("http://192.168.0.10/secure", timeout=0.01)
except requests.exceptions.Timeout:
print("timeout http request to secure")
pass
else:
ret = os.system("nmcli d wifi connect %s password %s" % (ssid, pw))
if os.WEXITSTATUS(ret) == 0:
#check ping too
ping_ok = False
connect_cnt = 0
MAX_TRIES = 10
while connect_cnt < MAX_TRIES:
connect_cnt += 1
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if r:
print("Waiting for panda to ping...")
time.sleep(0.1)
else:
ping_ok = True
break
if ping_ok:
break
# TODO: confirm that it's connected to the right panda
def time_many_sends(p, bus, precv=None, msg_count=100, msg_id=None, two_pandas=False):
if precv == None:
precv = p
if msg_id == None: if msg_id == None:
msg_id = random.randint(0x100, 0x200) msg_id = random.randint(0x100, 0x200)
if p == precv and two_pandas: if p == p_recv and two_pandas:
raise ValueError("Cannot have two pandas that are the same panda") raise ValueError("Cannot have two pandas that are the same panda")
st = time.time() start_time = time.time()
p.can_send_many([(msg_id, 0, b"\xaa"*8, bus)]*msg_count) p.can_send_many([(msg_id, 0, b"\xaa"*8, bus)]*msg_count)
r = [] r = []
r_echo = [] r_echo = []
r_len_expected = msg_count if two_pandas else msg_count*2 r_len_expected = msg_count if two_pandas else msg_count*2
r_echo_len_exected = msg_count if two_pandas else 0 r_echo_len_exected = msg_count if two_pandas else 0
while len(r) < r_len_expected and (time.time() - st) < 5: while len(r) < r_len_expected and (time.time() - start_time) < 5:
r.extend(precv.can_recv()) r.extend(p_recv.can_recv())
et = time.time() end_time = time.time()
if two_pandas: if two_pandas:
while len(r_echo) < r_echo_len_exected and (time.time() - st) < 10: while len(r_echo) < r_echo_len_exected and (time.time() - start_time) < 10:
r_echo.extend(p.can_recv()) r_echo.extend(p.can_recv())
sent_echo = [x for x in r if x[3] == 0x80 | bus and x[0] == msg_id] sent_echo = [x for x in r if x[3] == 0x80 | bus and x[0] == msg_id]
@ -164,12 +105,17 @@ def time_many_sends(p, bus, precv=None, msg_count=100, msg_id=None, two_pandas=F
assert_equal(len(resp), msg_count) assert_equal(len(resp), msg_count)
assert_equal(len(sent_echo), msg_count) assert_equal(len(sent_echo), msg_count)
et = (et-st)*1000.0 end_time = (end_time-start_time)*1000.0
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7)*msg_count / et comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7)*msg_count / end_time
return comp_kbps return comp_kbps
_panda_serials = None def reset_pandas():
panda_jungle.set_panda_power(False)
time.sleep(2)
panda_jungle.set_panda_power(True)
time.sleep(5)
def panda_type_to_serial(fn): def panda_type_to_serial(fn):
@wraps(fn) @wraps(fn)
def wrapper(panda_type=None, **kwargs): def wrapper(panda_type=None, **kwargs):
@ -181,11 +127,7 @@ def panda_type_to_serial(fn):
# If not done already, get panda serials and their type # If not done already, get panda serials and their type
global _panda_serials global _panda_serials
if _panda_serials == None: if _panda_serials == None:
_panda_serials = [] init_panda_serials()
for serial in Panda.list():
p = Panda(serial=serial)
_panda_serials.append((serial, p.get_type()))
p.close()
# Find a panda with the correct types and add the corresponding serial # Find a panda with the correct types and add the corresponding serial
serials = [] serials = []
@ -202,13 +144,15 @@ def panda_type_to_serial(fn):
return fn(serials, **kwargs) return fn(serials, **kwargs)
return wrapper return wrapper
def heartbeat_thread(p): def start_heartbeat_thread(p):
while True: def heartbeat_thread(p):
try: while True:
p.send_heartbeat() try:
time.sleep(1) p.send_heartbeat()
except: time.sleep(1)
break except:
break
_thread.start_new_thread(heartbeat_thread, (p,))
def panda_connect_and_init(fn): def panda_connect_and_init(fn):
@wraps(fn) @wraps(fn)
@ -223,25 +167,38 @@ def panda_connect_and_init(fn):
for panda_serial in panda_serials: for panda_serial in panda_serials:
pandas.append(Panda(serial=panda_serial)) pandas.append(Panda(serial=panda_serial))
# Initialize jungle
clear_can_buffers(panda_jungle)
panda_jungle.set_can_loopback(False)
panda_jungle.set_obd(False)
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1)
for bus, speed in BUS_SPEEDS:
panda_jungle.set_can_speed_kbps(bus, speed)
# Initialize pandas # Initialize pandas
for panda in pandas: for panda in pandas:
panda.set_can_loopback(False) panda.set_can_loopback(False)
panda.set_gmlan(None) panda.set_gmlan(None)
panda.set_esp_power(False) panda.set_esp_power(False)
for bus, speed in [(0, SPEED_NORMAL), (1, SPEED_NORMAL), (2, SPEED_NORMAL), (3, SPEED_GMLAN)]: panda.set_power_save(False)
for bus, speed in BUS_SPEEDS:
panda.set_can_speed_kbps(bus, speed) panda.set_can_speed_kbps(bus, speed)
clear_can_buffers(panda) clear_can_buffers(panda)
_thread.start_new_thread(heartbeat_thread, (panda,)) panda.set_power_save(False)
# Run test function
ret = fn(*pandas, **kwargs)
# Close all connections try:
for panda in pandas: run_with_timeout(TIMEOUT, fn, *pandas, **kwargs)
panda.close()
# Check if the pandas did not throw any faults while running test
# Return test function result for panda in pandas:
return ret panda.reconnect()
assert panda.health()['fault_status'] == 0
except Exception as e:
raise e
finally:
# Close all connections
for panda in pandas:
panda.close()
return wrapper return wrapper
def clear_can_buffers(panda): def clear_can_buffers(panda):

@ -0,0 +1,25 @@
import time
from multiprocessing import Process
# Note: this does not return any return values of the function, just the exit status
INTERVAL = 0.1
def run_with_timeout(timeout, fn, *kwargs):
def runner(fn, kwargs):
try:
fn(*kwargs)
except Exception as e:
print(e)
raise e
process = Process(target=runner, args=(fn, kwargs))
process.start()
counter = 0
while process.is_alive():
time.sleep(INTERVAL)
counter+=1
if (counter * INTERVAL) > timeout:
process.terminate()
raise TimeoutError("Function timed out!")
if process.exitcode != 0:
raise RuntimeError("Test failed with exit code: ", str(process.exitcode))

@ -0,0 +1,85 @@
import os
FNULL = open(os.devnull, 'w')
def _connect_wifi(dongle_id, pw, insecure_okay=False):
ssid = "panda-" + dongle_id
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if not r:
# Can already ping, try connecting on wifi
try:
p = Panda("WIFI")
p.get_serial()
print("Already connected")
return
except:
pass
print("WIFI: connecting to %s" % ssid)
while 1:
if sys.platform == "darwin":
os.system("networksetup -setairportnetwork en0 %s %s" % (ssid, pw))
else:
wlan_interface = subprocess.check_output(["sh", "-c", "iw dev | awk '/Interface/ {print $2}'"]).strip().decode('utf8')
cnt = 0
MAX_TRIES = 10
while cnt < MAX_TRIES:
print("WIFI: scanning %d" % cnt)
os.system("iwlist %s scanning > /dev/null" % wlan_interface)
os.system("nmcli device wifi rescan")
wifi_networks = [x.decode("utf8") for x in subprocess.check_output(["nmcli","dev", "wifi", "list"]).split(b"\n")]
wifi_scan = [x for x in wifi_networks if ssid in x]
if len(wifi_scan) != 0:
break
time.sleep(0.1)
# MAX_TRIES tries, ~10 seconds max
cnt += 1
assert cnt < MAX_TRIES
if "-pair" in wifi_scan[0]:
os.system("nmcli d wifi connect %s-pair" % (ssid))
connect_cnt = 0
MAX_TRIES = 100
while connect_cnt < MAX_TRIES:
connect_cnt += 1
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if r:
print("Waiting for panda to ping...")
time.sleep(0.5)
else:
break
if insecure_okay:
break
# fetch webpage
print("connecting to insecure network to secure")
try:
r = requests.get("http://192.168.0.10/")
except requests.ConnectionError:
r = requests.get("http://192.168.0.10/")
assert r.status_code==200
print("securing")
try:
r = requests.get("http://192.168.0.10/secure", timeout=0.01)
except requests.exceptions.Timeout:
print("timeout http request to secure")
pass
else:
ret = os.system("nmcli d wifi connect %s password %s" % (ssid, pw))
if os.WEXITSTATUS(ret) == 0:
#check ping too
ping_ok = False
connect_cnt = 0
MAX_TRIES = 10
while connect_cnt < MAX_TRIES:
connect_cnt += 1
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if r:
print("Waiting for panda to ping...")
time.sleep(0.1)
else:
ping_ok = True
break
if ping_ok:
break
# TODO: confirm that it's connected to the right panda

@ -67,7 +67,7 @@ def run_test(sleep_duration):
print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors) print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors)
# Toggle relay # Toggle relay
black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT) black_panda.set_safety_mode(Panda.SAFETY_SILENT)
time.sleep(1) time.sleep(1)
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(1) time.sleep(1)

@ -72,7 +72,7 @@ def run_test(sleep_duration):
if (time.time() - temp_start_time) > 3600*6: if (time.time() - temp_start_time) > 3600*6:
# Toggle relay # Toggle relay
black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT) black_panda.set_safety_mode(Panda.SAFETY_SILENT)
time.sleep(1) time.sleep(1)
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(1) time.sleep(1)

@ -74,7 +74,7 @@ def run_test(sleep_duration):
assert False assert False
# Switch off relay # Switch off relay
black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT) black_panda.set_safety_mode(Panda.SAFETY_SILENT)
time.sleep(0.05) time.sleep(0.05)
if not test_buses(black_panda, other_panda, (0, False, [0, 2])): if not test_buses(black_panda, other_panda, (0, False, [0, 2])):

@ -44,6 +44,6 @@ if __name__ == "__main__":
if claim: if claim:
panda.serial_write(port_number, ln) panda.serial_write(port_number, ln)
time.sleep(0.01) time.sleep(0.01)
except: except Exception:
print("panda disconnected!") print("panda disconnected!")
time.sleep(0.5); time.sleep(0.5);

@ -0,0 +1,50 @@
#!/usr/bin/env python3
import matplotlib.pyplot as plt # pylint: disable=import-error
HASHING_PRIME = 23
REGISTER_MAP_SIZE = 0x3FF
BYTES_PER_REG = 4
# From ST32F413 datasheet
REGISTER_ADDRESS_REGIONS = [
(0x40000000, 0x40007FFF),
(0x40010000, 0x400107FF),
(0x40011000, 0x400123FF),
(0x40012C00, 0x40014BFF),
(0x40015000, 0x400153FF),
(0x40015800, 0x40015BFF),
(0x40016000, 0x400167FF),
(0x40020000, 0x40021FFF),
(0x40023000, 0x400233FF),
(0x40023800, 0x40023FFF),
(0x40026000, 0x400267FF),
(0x50000000, 0x5003FFFF),
(0x50060000, 0x500603FF),
(0x50060800, 0x50060BFF),
(0x50060800, 0x50060BFF),
(0xE0000000, 0xE00FFFFF)
]
def hash(reg_addr):
return (((reg_addr >> 16) ^ ((((reg_addr + 1) & 0xFFFF) * HASHING_PRIME) & 0xFFFF)) & REGISTER_MAP_SIZE)
# Calculate hash for each address
hashes = []
double_hashes = []
for (start_addr, stop_addr) in REGISTER_ADDRESS_REGIONS:
for addr in range(start_addr, stop_addr+1, BYTES_PER_REG):
h = hash(addr)
hashes.append(h)
double_hashes.append(hash(h))
# Make histograms
plt.subplot(2, 1, 1)
plt.hist(hashes, bins=REGISTER_MAP_SIZE)
plt.title("Number of collisions per hash")
plt.xlabel("Address")
plt.subplot(2, 1, 2)
plt.hist(double_hashes, bins=REGISTER_MAP_SIZE)
plt.title("Number of collisions per double hash")
plt.xlabel("Address")
plt.show()

@ -0,0 +1,35 @@
#!/usr/bin/env python3
import os
import sys
import time
import _thread
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
# This script is intended to be used in conjunction with the echo_loopback_test.py test script from panda jungle.
# It sends a reversed response back for every message received containing b"test".
def heartbeat_thread(p):
while True:
try:
p.send_heartbeat()
time.sleep(1)
except:
break
# Resend every CAN message that has been received on the same bus, but with the data reversed
if __name__ == "__main__":
p = Panda()
_thread.start_new_thread(heartbeat_thread, (p,))
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
while True:
incoming = p.can_recv()
for message in incoming:
address, notused, data, bus = message
if b'test' in data:
p.can_send(address, data[::-1], bus)

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import os import os
import sys import sys
import time import time

@ -0,0 +1 @@
cppcheck/

@ -7,7 +7,7 @@
2.4 X (Cppcheck) 2.4 X (Cppcheck)
2.5 2.5
2.6 X (Cppcheck) 2.6 X (Cppcheck)
2.7 2.7 X (Addon)
3.1 X (Addon) 3.1 X (Addon)
3.2 X (Addon) 3.2 X (Addon)
4.1 X (Addon) 4.1 X (Addon)

@ -4,7 +4,7 @@ mkdir /tmp/misra || true
git clone https://github.com/danmar/cppcheck.git || true git clone https://github.com/danmar/cppcheck.git || true
cd cppcheck cd cppcheck
git fetch git fetch
git checkout bdd41151ed550e3d240a6dd6847859216b7ad736 git checkout e46191e6e809272d8b34feca8999ee413f716b80
make -j4 make -j4
cd ../../../ cd ../../../

@ -0,0 +1,36 @@
from panda.tests.safety import libpandasafety_py
def make_msg(bus, addr, length=8):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
if addr >= 0x800:
to_send[0].RIR = (addr << 3) | 5
else:
to_send[0].RIR = (addr << 21) | 1
to_send[0].RDTR = length
to_send[0].RDTR |= bus << 4
return to_send
def test_relay_malfunction(test, addr):
# input is a test class and the address that, if seen on bus 0, triggers
# the relay_malfunction protection logic: both tx_hook and fwd_hook are
# expected to return failure
test.assertFalse(test.safety.get_relay_malfunction())
test.safety.safety_rx_hook(make_msg(0, addr, 8))
test.assertTrue(test.safety.get_relay_malfunction())
for a in range(1, 0x800):
for b in range(0, 3):
test.assertFalse(test.safety.safety_tx_hook(make_msg(b, a, 8)))
test.assertEqual(-1, test.safety.safety_fwd_hook(b, make_msg(b, a, 8)))
def test_manually_enable_controls_allowed(test):
test.safety.set_controls_allowed(1)
test.assertTrue(test.safety.get_controls_allowed())
test.safety.set_controls_allowed(0)
test.assertFalse(test.safety.get_controls_allowed())
def test_spam_can_buses(test, TX_MSGS):
for addr in range(1, 0x800):
for bus in range(0, 4):
if all(addr != m[0] or bus != m[1] for m in TX_MSGS):
test.assertFalse(test.safety.safety_tx_hook(make_msg(bus, addr, 8)))

@ -34,6 +34,8 @@ bool board_has_relay(void);
void set_controls_allowed(bool c); void set_controls_allowed(bool c);
bool get_controls_allowed(void); bool get_controls_allowed(void);
void set_relay_malfunction(bool c);
bool get_relay_malfunction(void);
void set_long_controls_allowed(bool c); void set_long_controls_allowed(bool c);
bool get_long_controls_allowed(void); bool get_long_controls_allowed(void);
void set_gas_interceptor_detected(bool c); void set_gas_interceptor_detected(bool c);
@ -46,7 +48,7 @@ void reset_angle_control(void);
void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_send); void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_send);
int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_push); int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_push);
int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd); int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd);
int safety_set_mode(uint16_t mode, int16_t param); int set_safety_hooks(uint16_t mode, int16_t param);
void init_tests_toyota(void); void init_tests_toyota(void);
int get_toyota_torque_meas_min(void); int get_toyota_torque_meas_min(void);
@ -54,7 +56,6 @@ int get_toyota_torque_meas_max(void);
int get_toyota_gas_prev(void); int get_toyota_gas_prev(void);
void set_toyota_torque_meas(int min, int max); void set_toyota_torque_meas(int min, int max);
void set_toyota_desired_torque_last(int t); void set_toyota_desired_torque_last(int t);
void set_toyota_camera_forwarded(int t);
void set_toyota_rt_torque_last(int t); void set_toyota_rt_torque_last(int t);
void init_tests_honda(void); void init_tests_honda(void);
@ -80,13 +81,10 @@ void init_tests_hyundai(void);
void set_hyundai_desired_torque_last(int t); void set_hyundai_desired_torque_last(int t);
void set_hyundai_rt_torque_last(int t); void set_hyundai_rt_torque_last(int t);
void set_hyundai_torque_driver(int min, int max); void set_hyundai_torque_driver(int min, int max);
void set_hyundai_giraffe_switch_2(int t);
void set_hyundai_camera_bus(int t);
void init_tests_chrysler(void); void init_tests_chrysler(void);
void set_chrysler_desired_torque_last(int t); void set_chrysler_desired_torque_last(int t);
void set_chrysler_rt_torque_last(int t); void set_chrysler_rt_torque_last(int t);
void set_chrysler_camera_detected(int t);
int get_chrysler_torque_meas_min(void); int get_chrysler_torque_meas_min(void);
int get_chrysler_torque_meas_max(void); int get_chrysler_torque_meas_max(void);
void set_chrysler_torque_meas(int min, int max); void set_chrysler_torque_meas(int min, int max);

@ -42,6 +42,8 @@ TIM_TypeDef *TIM2 = &timer;
#define HW_TYPE_PEDAL 4U #define HW_TYPE_PEDAL 4U
#define HW_TYPE_UNO 5U #define HW_TYPE_UNO 5U
#define ALLOW_DEBUG
// from main_declarations.h // from main_declarations.h
uint8_t hw_type = HW_TYPE_UNKNOWN; uint8_t hw_type = HW_TYPE_UNKNOWN;
@ -80,6 +82,10 @@ void set_controls_allowed(bool c){
controls_allowed = c; controls_allowed = c;
} }
void set_relay_malfunction(bool c){
relay_malfunction = c;
}
void set_long_controls_allowed(bool c){ void set_long_controls_allowed(bool c){
long_controls_allowed = c; long_controls_allowed = c;
} }
@ -96,6 +102,10 @@ bool get_controls_allowed(void){
return controls_allowed; return controls_allowed;
} }
bool get_relay_malfunction(void){
return relay_malfunction;
}
bool get_long_controls_allowed(void){ bool get_long_controls_allowed(void){
return long_controls_allowed; return long_controls_allowed;
} }
@ -116,10 +126,6 @@ void set_timer(uint32_t t){
timer.CNT = t; timer.CNT = t;
} }
void set_toyota_camera_forwarded(int t){
toyota_camera_forwarded = t;
}
void set_toyota_torque_meas(int min, int max){ void set_toyota_torque_meas(int min, int max){
toyota_torque_meas.min = min; toyota_torque_meas.min = min;
toyota_torque_meas.max = max; toyota_torque_meas.max = max;
@ -140,18 +146,6 @@ void set_hyundai_torque_driver(int min, int max){
hyundai_torque_driver.max = max; hyundai_torque_driver.max = max;
} }
void set_hyundai_camera_bus(int t){
hyundai_camera_bus = t;
}
void set_hyundai_giraffe_switch_2(int t){
hyundai_giraffe_switch_2 = t;
}
void set_chrysler_camera_detected(int t){
chrysler_camera_detected = t;
}
void set_chrysler_torque_meas(int min, int max){ void set_chrysler_torque_meas(int min, int max){
chrysler_torque_meas.min = min; chrysler_torque_meas.min = min;
chrysler_torque_meas.max = max; chrysler_torque_meas.max = max;
@ -278,6 +272,7 @@ void set_honda_fwd_brake(bool c){
void init_tests(void){ void init_tests(void){
// get HW_TYPE from env variable set in test.sh // get HW_TYPE from env variable set in test.sh
hw_type = atoi(getenv("HW_TYPE")); hw_type = atoi(getenv("HW_TYPE"));
safety_mode_cnt = 2U; // avoid ignoring relay_malfunction logic
} }
void init_tests_toyota(void){ void init_tests_toyota(void){

@ -1,8 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 2 MAX_RATE_UP = 2
MAX_RATE_DOWN = 5 MAX_RATE_DOWN = 5
@ -16,6 +18,8 @@ DRIVER_TORQUE_FACTOR = 4;
IPAS_OVERRIDE_THRESHOLD = 200 IPAS_OVERRIDE_THRESHOLD = 200
TX_MSGS = [[0x151, 2], [0x152, 0], [0x153, 2], [0x154, 0]]
def twos_comp(val, bits): def twos_comp(val, bits):
if val >= 0: if val >= 0:
return val return val
@ -32,59 +36,42 @@ class TestCadillacSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_CADILLAC, 0) cls.safety.set_safety_hooks(Panda.SAFETY_CADILLAC, 0)
cls.safety.init_tests_cadillac() cls.safety.init_tests_cadillac()
def _send_msg(self, bus, addr, length):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = length
to_send[0].RDTR = bus << 4
return to_send
def _set_prev_torque(self, t): def _set_prev_torque(self, t):
self.safety.set_cadillac_desired_torque_last(t) self.safety.set_cadillac_desired_torque_last(t)
self.safety.set_cadillac_rt_torque_last(t) self.safety.set_cadillac_rt_torque_last(t)
def _torque_driver_msg(self, torque): def _torque_driver_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = 0x164 << 21
t = twos_comp(torque, 11) t = twos_comp(torque, 11)
to_send = make_msg(0, 0x164)
to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8) to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8)
return to_send return to_send
def _torque_msg(self, torque): def _torque_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(2, 0x151)
to_send[0].RIR = 0x151 << 21
t = twos_comp(torque, 14) t = twos_comp(torque, 14)
to_send[0].RDLR = ((t >> 8) & 0x3F) | ((t & 0xFF) << 8) to_send[0].RDLR = ((t >> 8) & 0x3F) | ((t & 0xFF) << 8)
return to_send return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_default_controls_not_allowed(self): def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
def test_manually_enable_controls_allowed(self): def test_manually_enable_controls_allowed(self):
self.safety.set_controls_allowed(1) test_manually_enable_controls_allowed(self)
self.assertTrue(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(0)
def test_enable_control_allowed_from_cruise(self): def test_enable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 0x370)
to_push[0].RIR = 0x370 << 21
to_push[0].RDLR = 0x800000 to_push[0].RDLR = 0x800000
to_push[0].RDTR = 0
self.safety.safety_rx_hook(to_push) self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed()) self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self): def test_disable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 0x370)
to_push[0].RIR = 0x370 << 21
to_push[0].RDLR = 0
to_push[0].RDTR = 0
self.safety.set_controls_allowed(1) self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push) self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
@ -190,7 +177,7 @@ class TestCadillacSafety(unittest.TestCase):
for b in buss: for b in buss:
for m in msgs: for m in msgs:
# assume len 8 # assume len 8
self.assertEqual(-1, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) self.assertEqual(-1, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 3 MAX_RATE_UP = 3
MAX_RATE_DOWN = 3 MAX_RATE_DOWN = 3
@ -13,40 +14,17 @@ RT_INTERVAL = 250000
MAX_TORQUE_ERROR = 80 MAX_TORQUE_ERROR = 80
def twos_comp(val, bits): TX_MSGS = [[571, 0], [658, 0], [678, 0]]
if val >= 0:
return val
else:
return (2**bits) + val
def sign(a):
if a > 0:
return 1
else:
return -1
def swap_bytes(data_str):
"""Accepts string with hex, returns integer with order swapped for CAN."""
a = int(data_str, 16)
return ((a & 0xff) << 24) + ((a & 0xff00) << 8) + ((a & 0x00ff0000) >> 8) + ((a & 0xff000000) >> 24)
class TestChryslerSafety(unittest.TestCase): class TestChryslerSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_CHRYSLER, 0) cls.safety.set_safety_hooks(Panda.SAFETY_CHRYSLER, 0)
cls.safety.init_tests_chrysler() cls.safety.init_tests_chrysler()
def _send_msg(self, bus, addr, length):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = length
to_send[0].RDTR = bus << 4
return to_send
def _button_msg(self, buttons): def _button_msg(self, buttons):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 571)
to_send[0].RIR = 1265 << 21
to_send[0].RDLR = buttons to_send[0].RDLR = buttons
return to_send return to_send
@ -56,17 +34,21 @@ class TestChryslerSafety(unittest.TestCase):
self.safety.set_chrysler_torque_meas(t, t) self.safety.set_chrysler_torque_meas(t, t)
def _torque_meas_msg(self, torque): def _torque_meas_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 544)
to_send[0].RIR = 544 << 21
to_send[0].RDHR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8) to_send[0].RDHR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8)
return to_send return to_send
def _torque_msg(self, torque): def _torque_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x292)
to_send[0].RIR = 0x292 << 21
to_send[0].RDLR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8) to_send[0].RDLR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8)
return to_send return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0x292)
def test_default_controls_not_allowed(self): def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
@ -81,22 +63,17 @@ class TestChryslerSafety(unittest.TestCase):
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self): def test_manually_enable_controls_allowed(self):
self.safety.set_controls_allowed(1) test_manually_enable_controls_allowed(self)
self.assertTrue(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(0)
self.assertFalse(self.safety.get_controls_allowed())
def test_enable_control_allowed_from_cruise(self): def test_enable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 0x1F4)
to_push[0].RIR = 0x1f4 << 21
to_push[0].RDLR = 0x380000 to_push[0].RDLR = 0x380000
self.safety.safety_rx_hook(to_push) self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed()) self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self): def test_disable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 0x1F4)
to_push[0].RIR = 0x1f4 << 21
to_push[0].RDLR = 0 to_push[0].RDLR = 0
self.safety.set_controls_allowed(1) self.safety.set_controls_allowed(1)
@ -179,28 +156,30 @@ class TestChryslerSafety(unittest.TestCase):
self.assertEqual(0, self.safety.get_chrysler_torque_meas_max()) self.assertEqual(0, self.safety.get_chrysler_torque_meas_max())
self.assertEqual(0, self.safety.get_chrysler_torque_meas_min()) self.assertEqual(0, self.safety.get_chrysler_torque_meas_min())
def test_cancel_button(self):
CANCEL = 1
for b in range(0, 0xff):
if b == CANCEL:
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(b)))
else:
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(b)))
def test_fwd_hook(self): def test_fwd_hook(self):
buss = list(range(0x0, 0x3)) buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800)) msgs = list(range(0x1, 0x800))
chrysler_camera_detected = [0, 1]
blocked_msgs = [658, 678]
for ccd in chrysler_camera_detected: for b in buss:
self.safety.set_chrysler_camera_detected(ccd) for m in msgs:
blocked_msgs = [658, 678] if b == 0:
for b in buss: fwd_bus = 2
for m in msgs: elif b == 1:
if not ccd: fwd_bus = -1
if b == 0: elif b == 2:
fwd_bus = 2 fwd_bus = -1 if m in blocked_msgs else 0
elif b == 1:
fwd_bus = -1 # assume len 8
elif b == 2: self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
fwd_bus = -1 if m in blocked_msgs else 0
else:
fwd_bus = -1
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8)))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 7 MAX_RATE_UP = 7
MAX_RATE_DOWN = 17 MAX_RATE_DOWN = 17
@ -17,6 +18,11 @@ RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 50; DRIVER_TORQUE_ALLOWANCE = 50;
DRIVER_TORQUE_FACTOR = 4; DRIVER_TORQUE_FACTOR = 4;
TX_MSGS = [[384, 0], [1033, 0], [1034, 0], [715, 0], [880, 0], # pt bus
[161, 1], [774, 1], [776, 1], [784, 1], # obs bus
[789, 2], # ch bus
[0x104c006c, 3], [0x10400060]] # gmlan
def twos_comp(val, bits): def twos_comp(val, bits):
if val >= 0: if val >= 0:
return val return val
@ -33,50 +39,37 @@ class TestGmSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_GM, 0) cls.safety.set_safety_hooks(Panda.SAFETY_GM, 0)
cls.safety.init_tests_gm() cls.safety.init_tests_gm()
def _send_msg(self, bus, addr, length):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = length
to_send[0].RDTR = bus << 4
return to_send
def _speed_msg(self, speed): def _speed_msg(self, speed):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 842)
to_send[0].RIR = 842 << 21
to_send[0].RDLR = speed to_send[0].RDLR = speed
return to_send return to_send
def _button_msg(self, buttons): def _button_msg(self, buttons):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 481)
to_send[0].RIR = 481 << 21
to_send[0].RDHR = buttons << 12 to_send[0].RDHR = buttons << 12
return to_send return to_send
def _brake_msg(self, brake): def _brake_msg(self, brake):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 241)
to_send[0].RIR = 241 << 21
to_send[0].RDLR = 0xa00 if brake else 0x900 to_send[0].RDLR = 0xa00 if brake else 0x900
return to_send return to_send
def _gas_msg(self, gas): def _gas_msg(self, gas):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 417)
to_send[0].RIR = 417 << 21
to_send[0].RDHR = (1 << 16) if gas else 0 to_send[0].RDHR = (1 << 16) if gas else 0
return to_send return to_send
def _send_brake_msg(self, brake): def _send_brake_msg(self, brake):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(2, 789)
to_send[0].RIR = 789 << 21
brake = (-brake) & 0xfff brake = (-brake) & 0xfff
to_send[0].RDLR = (brake >> 8) | ((brake &0xff) << 8) to_send[0].RDLR = (brake >> 8) | ((brake &0xff) << 8)
return to_send return to_send
def _send_gas_msg(self, gas): def _send_gas_msg(self, gas):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 715)
to_send[0].RIR = 715 << 21
to_send[0].RDLR = ((gas & 0x1f) << 27) | ((gas & 0xfe0) << 11) to_send[0].RDLR = ((gas & 0x1f) << 27) | ((gas & 0xfe0) << 11)
return to_send return to_send
@ -85,21 +78,23 @@ class TestGmSafety(unittest.TestCase):
self.safety.set_gm_rt_torque_last(t) self.safety.set_gm_rt_torque_last(t)
def _torque_driver_msg(self, torque): def _torque_driver_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = 388 << 21
t = twos_comp(torque, 11) t = twos_comp(torque, 11)
to_send = make_msg(0, 388)
to_send[0].RDHR = (((t >> 8) & 0x7) << 16) | ((t & 0xFF) << 24) to_send[0].RDHR = (((t >> 8) & 0x7) << 16) | ((t & 0xFF) << 24)
return to_send return to_send
def _torque_msg(self, torque): def _torque_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = 384 << 21
t = twos_comp(torque, 11) t = twos_comp(torque, 11)
to_send = make_msg(0, 384)
to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8) to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8)
return to_send return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 384)
def test_default_controls_not_allowed(self): def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
@ -198,10 +193,7 @@ class TestGmSafety(unittest.TestCase):
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self): def test_manually_enable_controls_allowed(self):
self.safety.set_controls_allowed(1) test_manually_enable_controls_allowed(self)
self.assertTrue(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(0)
self.assertFalse(self.safety.get_controls_allowed())
def test_non_realtime_limit_up(self): def test_non_realtime_limit_up(self):
self.safety.set_gm_torque_driver(0, 0) self.safety.set_gm_torque_driver(0, 0)
@ -289,7 +281,7 @@ class TestGmSafety(unittest.TestCase):
for b in buss: for b in buss:
for m in msgs: for m in msgs:
# assume len 8 # assume len 8
self.assertEqual(-1, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) self.assertEqual(-1, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,94 +1,79 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_BRAKE = 255 MAX_BRAKE = 255
INTERCEPTOR_THRESHOLD = 328 INTERCEPTOR_THRESHOLD = 328
TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x200, 0], [0x30C, 0], [0x33D, 0], [0x39F, 0]]
class TestHondaSafety(unittest.TestCase): class TestHondaSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_HONDA, 0) cls.safety.set_safety_hooks(Panda.SAFETY_HONDA, 0)
cls.safety.init_tests_honda() cls.safety.init_tests_honda()
def _send_msg(self, bus, addr, length):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = length
to_send[0].RDTR = bus << 4
return to_send
def _speed_msg(self, speed): def _speed_msg(self, speed):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x158)
to_send[0].RIR = 0x158 << 21
to_send[0].RDLR = speed to_send[0].RDLR = speed
return to_send return to_send
def _button_msg(self, buttons, msg): def _button_msg(self, buttons, msg):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = msg << 21
to_send[0].RDLR = buttons << 5
has_relay = self.safety.board_has_relay() has_relay = self.safety.board_has_relay()
honda_bosch_hardware = self.safety.get_honda_bosch_hardware() honda_bosch_hardware = self.safety.get_honda_bosch_hardware()
bus = 1 if has_relay and honda_bosch_hardware else 0 bus = 1 if has_relay and honda_bosch_hardware else 0
to_send[0].RDTR = bus << 4 to_send = make_msg(bus, msg)
to_send[0].RDLR = buttons << 5
return to_send return to_send
def _brake_msg(self, brake): def _brake_msg(self, brake):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x17C)
to_send[0].RIR = 0x17C << 21
to_send[0].RDHR = 0x200000 if brake else 0 to_send[0].RDHR = 0x200000 if brake else 0
return to_send return to_send
def _alt_brake_msg(self, brake): def _alt_brake_msg(self, brake):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x1BE)
to_send[0].RIR = 0x1BE << 21
to_send[0].RDLR = 0x10 if brake else 0 to_send[0].RDLR = 0x10 if brake else 0
return to_send return to_send
def _gas_msg(self, gas): def _gas_msg(self, gas):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x17C)
to_send[0].RIR = 0x17C << 21
to_send[0].RDLR = 1 if gas else 0 to_send[0].RDLR = 1 if gas else 0
return to_send return to_send
def _send_brake_msg(self, brake): def _send_brake_msg(self, brake):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x1FA)
to_send[0].RIR = 0x1FA << 21
to_send[0].RDLR = ((brake & 0x3) << 14) | ((brake & 0x3FF) >> 2) to_send[0].RDLR = ((brake & 0x3) << 14) | ((brake & 0x3FF) >> 2)
return to_send return to_send
def _send_interceptor_msg(self, gas, addr): def _send_interceptor_msg(self, gas, addr):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, addr, 6)
to_send[0].RIR = addr << 21
to_send[0].RDTR = 6
gas2 = gas * 2 gas2 = gas * 2
to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \ to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \
((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8) ((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8)
return to_send return to_send
def _send_steer_msg(self, steer): def _send_steer_msg(self, steer):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0xE4, 6)
to_send[0].RIR = 0xE4 << 21
to_send[0].RDLR = steer to_send[0].RDLR = steer
return to_send return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0xE4)
def test_default_controls_not_allowed(self): def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_resume_button(self): def test_resume_button(self):
RESUME_BTN = 4 RESUME_BTN = 4
self.safety.set_controls_allowed(0) self.safety.set_controls_allowed(0)
@ -281,7 +266,7 @@ class TestHondaSafety(unittest.TestCase):
fwd_bus = -1 if m in blocked_msgs else 0 fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8 # assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
self.safety.set_long_controls_allowed(True) self.safety.set_long_controls_allowed(True)
self.safety.set_honda_fwd_brake(False) self.safety.set_honda_fwd_brake(False)

@ -1,29 +1,31 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import make_msg, test_spam_can_buses
MAX_BRAKE = 255 MAX_BRAKE = 255
H_TX_MSGS = [[0xE4, 0], [0x296, 1], [0x33D, 0]] # Bosch Harness
G_TX_MSGS = [[0xE4, 2], [0x296, 0], [0x33D, 2]] # Bosch Giraffe
class TestHondaSafety(unittest.TestCase): class TestHondaSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_HONDA_BOSCH, 0) cls.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH, 0)
cls.safety.init_tests_honda() cls.safety.init_tests_honda()
def _send_msg(self, bus, addr, length): def test_spam_can_buses(self):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') if self.safety.board_has_relay():
to_send[0].RIR = addr << 21 test_spam_can_buses(self, H_TX_MSGS)
to_send[0].RDTR = length else:
to_send[0].RDTR = bus << 4 test_spam_can_buses(self, G_TX_MSGS)
return to_send
def test_fwd_hook(self): def test_fwd_hook(self):
buss = range(0x0, 0x3) buss = range(0x0, 0x3)
msgs = range(0x1, 0x800) msgs = range(0x1, 0x800)
#has_relay = self.safety.get_hw_type() == 3 # black panda
has_relay = self.safety.board_has_relay() has_relay = self.safety.board_has_relay()
bus_rdr_cam = 2 if has_relay else 1 bus_rdr_cam = 2 if has_relay else 1
bus_rdr_car = 0 if has_relay else 2 bus_rdr_car = 0 if has_relay else 2
@ -40,7 +42,7 @@ class TestHondaSafety(unittest.TestCase):
fwd_bus = bus_rdr_cam fwd_bus = bus_rdr_cam
# assume len 8 # assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 3 MAX_RATE_UP = 3
MAX_RATE_DOWN = 7 MAX_RATE_DOWN = 7
@ -14,6 +15,8 @@ RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 50; DRIVER_TORQUE_ALLOWANCE = 50;
DRIVER_TORQUE_FACTOR = 2; DRIVER_TORQUE_FACTOR = 2;
TX_MSGS = [[832, 0], [1265, 0]]
def twos_comp(val, bits): def twos_comp(val, bits):
if val >= 0: if val >= 0:
return val return val
@ -30,19 +33,11 @@ class TestHyundaiSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_HYUNDAI, 0) cls.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, 0)
cls.safety.init_tests_hyundai() cls.safety.init_tests_hyundai()
def _send_msg(self, bus, addr, length):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = length
to_send[0].RDTR = bus << 4
return to_send
def _button_msg(self, buttons): def _button_msg(self, buttons):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 1265)
to_send[0].RIR = 1265 << 21
to_send[0].RDLR = buttons to_send[0].RDLR = buttons
return to_send return to_send
@ -51,17 +46,21 @@ class TestHyundaiSafety(unittest.TestCase):
self.safety.set_hyundai_rt_torque_last(t) self.safety.set_hyundai_rt_torque_last(t)
def _torque_driver_msg(self, torque): def _torque_driver_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 897)
to_send[0].RIR = 897 << 21
to_send[0].RDLR = (torque + 2048) << 11 to_send[0].RDLR = (torque + 2048) << 11
return to_send return to_send
def _torque_msg(self, torque): def _torque_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 832)
to_send[0].RIR = 832 << 21
to_send[0].RDLR = (torque + 1024) << 16 to_send[0].RDLR = (torque + 1024) << 16
return to_send return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 832)
def test_default_controls_not_allowed(self): def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
@ -76,24 +75,16 @@ class TestHyundaiSafety(unittest.TestCase):
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self): def test_manually_enable_controls_allowed(self):
self.safety.set_controls_allowed(1) test_manually_enable_controls_allowed(self)
self.assertTrue(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(0)
self.assertFalse(self.safety.get_controls_allowed())
def test_enable_control_allowed_from_cruise(self): def test_enable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 1057)
to_push[0].RIR = 1057 << 21
to_push[0].RDLR = 1 << 13 to_push[0].RDLR = 1 << 13
self.safety.safety_rx_hook(to_push) self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed()) self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self): def test_disable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 1057)
to_push[0].RIR = 1057 << 21
to_push[0].RDLR = 0
self.safety.set_controls_allowed(1) self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push) self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
@ -176,43 +167,35 @@ class TestHyundaiSafety(unittest.TestCase):
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
#def test_spam_cancel_safety_check(self): def test_spam_cancel_safety_check(self):
# RESUME_BTN = 1 RESUME_BTN = 1
# SET_BTN = 2 SET_BTN = 2
# CANCEL_BTN = 4 CANCEL_BTN = 4
# BUTTON_MSG = 1265 self.safety.set_controls_allowed(0)
# self.safety.set_controls_allowed(0) self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN)))
# self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN))) self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN)))
# self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN))) self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN)))
# self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN))) # do not block resume if we are engaged already
# # do not block resume if we are engaged already self.safety.set_controls_allowed(1)
# self.safety.set_controls_allowed(1) self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN)))
# self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN)))
def test_fwd_hook(self): def test_fwd_hook(self):
buss = list(range(0x0, 0x3)) buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800)) msgs = list(range(0x1, 0x800))
hyundai_giraffe_switch_2 = [0, 1]
blocked_msgs = [832]
self.safety.set_hyundai_camera_bus(2) for b in buss:
for hgs in hyundai_giraffe_switch_2: for m in msgs:
self.safety.set_hyundai_giraffe_switch_2(hgs) if b == 0:
blocked_msgs = [832] fwd_bus = 2
for b in buss: elif b == 1:
for m in msgs: fwd_bus = -1
if hgs: elif b == 2:
if b == 0: fwd_bus = -1 if m in blocked_msgs else 0
fwd_bus = 2
elif b == 1: # assume len 8
fwd_bus = -1 self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
elif b == 2:
fwd_bus = -1 if m in blocked_msgs else 0
else:
fwd_bus = -1
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8)))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 50 MAX_RATE_UP = 50
MAX_RATE_DOWN = 70 MAX_RATE_DOWN = 70
@ -14,6 +15,8 @@ RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 60; DRIVER_TORQUE_ALLOWANCE = 60;
DRIVER_TORQUE_FACTOR = 10; DRIVER_TORQUE_FACTOR = 10;
TX_MSGS = [[0x122, 0], [0x164, 0], [0x221, 0], [0x322, 0]]
def twos_comp(val, bits): def twos_comp(val, bits):
if val >= 0: if val >= 0:
return val return val
@ -30,52 +33,43 @@ class TestSubaruSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_SUBARU, 0) cls.safety.set_safety_hooks(Panda.SAFETY_SUBARU, 0)
cls.safety.init_tests_subaru() cls.safety.init_tests_subaru()
def _send_msg(self, bus, addr, length):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = length
to_send[0].RDTR = bus << 4
return to_send
def _set_prev_torque(self, t): def _set_prev_torque(self, t):
self.safety.set_subaru_desired_torque_last(t) self.safety.set_subaru_desired_torque_last(t)
self.safety.set_subaru_rt_torque_last(t) self.safety.set_subaru_rt_torque_last(t)
def _torque_driver_msg(self, torque): def _torque_driver_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = 0x119 << 21
t = twos_comp(torque, 11) t = twos_comp(torque, 11)
to_send = make_msg(0, 0x119)
to_send[0].RDLR = ((t & 0x7FF) << 16) to_send[0].RDLR = ((t & 0x7FF) << 16)
return to_send return to_send
def _torque_msg(self, torque): def _torque_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x122)
to_send[0].RIR = 0x122 << 21
t = twos_comp(torque, 13) t = twos_comp(torque, 13)
to_send[0].RDLR = (t << 16) to_send[0].RDLR = (t << 16)
return to_send return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0x122)
def test_default_controls_not_allowed(self): def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
def test_enable_control_allowed_from_cruise(self): def test_enable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 0x240)
to_push[0].RIR = 0x240 << 21
to_push[0].RDHR = 1 << 9 to_push[0].RDHR = 1 << 9
self.safety.safety_rx_hook(to_push) self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed()) self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self): def test_disable_control_allowed_from_cruise(self):
to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_push = make_msg(0, 0x240)
to_push[0].RIR = 0x240 << 21
to_push[0].RDHR = 0 to_push[0].RDHR = 0
self.safety.set_controls_allowed(1) self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push) self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
@ -91,10 +85,7 @@ class TestSubaruSafety(unittest.TestCase):
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self): def test_manually_enable_controls_allowed(self):
self.safety.set_controls_allowed(1) test_manually_enable_controls_allowed(self)
self.assertTrue(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(0)
self.assertFalse(self.safety.get_controls_allowed())
def test_non_realtime_limit_up(self): def test_non_realtime_limit_up(self):
self.safety.set_subaru_torque_driver(0, 0) self.safety.set_subaru_torque_driver(0, 0)
@ -188,7 +179,7 @@ class TestSubaruSafety(unittest.TestCase):
fwd_bus = -1 if m in blocked_msgs else 0 fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8 # assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
import libpandasafety_py # pylint: disable=import-error
from panda import Panda from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 10 MAX_RATE_UP = 10
MAX_RATE_DOWN = 25 MAX_RATE_DOWN = 25
@ -15,9 +16,14 @@ MAX_RT_DELTA = 375
RT_INTERVAL = 250000 RT_INTERVAL = 250000
MAX_TORQUE_ERROR = 350 MAX_TORQUE_ERROR = 350
INTERCEPTOR_THRESHOLD = 475 INTERCEPTOR_THRESHOLD = 475
TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0x344, 0], [0x365, 0], [0x366, 0], [0x4CB, 0], # DSU bus 0
[0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1
[0x2E4, 0], [0x411, 0], [0x412, 0], [0x343, 0], [0x1D2, 0], # LKAS + ACC
[0x200, 0]]; # interceptor
def twos_comp(val, bits): def twos_comp(val, bits):
if val >= 0: if val >= 0:
return val return val
@ -34,75 +40,60 @@ class TestToyotaSafety(unittest.TestCase):
@classmethod @classmethod
def setUp(cls): def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety cls.safety = libpandasafety_py.libpandasafety
cls.safety.safety_set_mode(Panda.SAFETY_TOYOTA, 100) cls.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, 100)
cls.safety.init_tests_toyota() cls.safety.init_tests_toyota()
def _send_msg(self, bus, addr, length):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = length
to_send[0].RDTR = bus << 4
return to_send
def _set_prev_torque(self, t): def _set_prev_torque(self, t):
self.safety.set_toyota_desired_torque_last(t) self.safety.set_toyota_desired_torque_last(t)
self.safety.set_toyota_rt_torque_last(t) self.safety.set_toyota_rt_torque_last(t)
self.safety.set_toyota_torque_meas(t, t) self.safety.set_toyota_torque_meas(t, t)
def _torque_meas_msg(self, torque): def _torque_meas_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = 0x260 << 21
t = twos_comp(torque, 16) t = twos_comp(torque, 16)
to_send = make_msg(0, 0x260)
to_send[0].RDHR = t | ((t & 0xFF) << 16) to_send[0].RDHR = t | ((t & 0xFF) << 16)
return to_send return to_send
def _torque_msg(self, torque): def _torque_msg(self, torque):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = 0x2E4 << 21
t = twos_comp(torque, 16) t = twos_comp(torque, 16)
to_send = make_msg(0, 0x2E4)
to_send[0].RDLR = t | ((t & 0xFF) << 16) to_send[0].RDLR = t | ((t & 0xFF) << 16)
return to_send return to_send
def _accel_msg(self, accel): def _accel_msg(self, accel):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x343)
to_send[0].RIR = 0x343 << 21
a = twos_comp(accel, 16) a = twos_comp(accel, 16)
to_send[0].RDLR = (a & 0xFF) << 8 | (a >> 8) to_send[0].RDLR = (a & 0xFF) << 8 | (a >> 8)
return to_send return to_send
def _send_gas_msg(self, gas): def _send_gas_msg(self, gas):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x2C1)
to_send[0].RIR = 0x2C1 << 21
to_send[0].RDHR = (gas & 0xFF) << 16 to_send[0].RDHR = (gas & 0xFF) << 16
return to_send return to_send
def _send_interceptor_msg(self, gas, addr): def _send_interceptor_msg(self, gas, addr):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = addr << 21
to_send[0].RDTR = 6
gas2 = gas * 2 gas2 = gas * 2
to_send = make_msg(0, addr, 6)
to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \ to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \
((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8) ((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8)
return to_send return to_send
def _pcm_cruise_msg(self, cruise_on): def _pcm_cruise_msg(self, cruise_on):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = make_msg(0, 0x1D2)
to_send[0].RIR = 0x1D2 << 21
to_send[0].RDLR = cruise_on << 5 to_send[0].RDLR = cruise_on << 5
return to_send return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0x2E4)
def test_default_controls_not_allowed(self): def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed()) self.assertFalse(self.safety.get_controls_allowed())
def test_manually_enable_controls_allowed(self): def test_manually_enable_controls_allowed(self):
self.safety.set_controls_allowed(1) test_manually_enable_controls_allowed(self)
self.assertTrue(self.safety.get_controls_allowed())
def test_enable_control_allowed_from_cruise(self): def test_enable_control_allowed_from_cruise(self):
self.safety.safety_rx_hook(self._pcm_cruise_msg(False)) self.safety.safety_rx_hook(self._pcm_cruise_msg(False))
@ -285,29 +276,23 @@ class TestToyotaSafety(unittest.TestCase):
buss = list(range(0x0, 0x3)) buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800)) msgs = list(range(0x1, 0x800))
long_controls_allowed = [0, 1] long_controls_allowed = [0, 1]
toyota_camera_forwarded = [0, 1]
for lca in long_controls_allowed:
for tcf in toyota_camera_forwarded: self.safety.set_long_controls_allowed(lca)
self.safety.set_toyota_camera_forwarded(tcf) blocked_msgs = [0x2E4, 0x412, 0x191]
for lca in long_controls_allowed: if lca:
self.safety.set_long_controls_allowed(lca) blocked_msgs += [0x343]
blocked_msgs = [0x2E4, 0x412, 0x191] for b in buss:
if lca: for m in msgs:
blocked_msgs += [0x343] if b == 0:
for b in buss: fwd_bus = 2
for m in msgs: elif b == 1:
if tcf: fwd_bus = -1
if b == 0: elif b == 2:
fwd_bus = 2 fwd_bus = -1 if m in blocked_msgs else 0
elif b == 1:
fwd_bus = -1 # assume len 8
elif b == 2: self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
fwd_bus = -1 if m in blocked_msgs else 0
else:
fwd_bus = -1
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8)))
self.safety.set_long_controls_allowed(True) self.safety.set_long_controls_allowed(True)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save