From bc7b9b38aeee676ff392b673bdcb6618a49c885d Mon Sep 17 00:00:00 2001 From: Vehicle Researcher Date: Fri, 13 Dec 2019 13:02:46 -0800 Subject: [PATCH] Squashed 'panda/' changes from 256d274e..a648ccae MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a648ccae Add os import 042562dd Extracted wifi connect from test helpers ac0fd5dd query fw versions example - use extended diagnostic session 4e9d788a Remove not-needed cadillac-init f0a5d154 typo c093286b Add bootkick after re-enabling phone power (#401) eadb0dbb security upgrades (#397) 7c13bec0 Command to get signature (#399) dad439a4 static assert on size of health packet (#398) da9da466 Fix VERSION df4159c8 Revert "Revert "Register readback on most modules. Still need to convert the other ones (#396)"" 56ec2150 Revert "Register readback on most modules. Still need to convert the other ones (#396)" 893e4861 Register readback on most modules. Still need to convert the other ones (#396) 6bbae7be VW safety: allow cancel spam on both buses to be compatible with camera and gateway integration d5f7a287 bump panda 1bcc351f ignition_can: set it to False after 2s of not seeing CAN msgs 96137f1a VW can based ignition not needed. it has ignition line at camera as well. 1b004a18 Same flake8 version as the one in openpilot e82ba5f2 Same pylint version as the one in openpilot 656f99b0 Interrupt refactor (NVIC_SM_1: #334) and Fault handling (#377) (PR #373) 000282e5 Fix can_logger.py to run correctly on python3 (#392) 7f9b4a59 Fix USB device enumeration on Windows 8.1 and Windows 10 (#393) dec565c7 Update Misra test coverage, which now includes rule 2.7 fb6bc3ba Fix Misra 878dd00a solve race condition is relay_malfunction right after changing the relay status by adding a counter 2d4cb05c add a safety mode counter a6797a21 Implement USB power mode on uno 670f90cc Merge branch 'master' of github.com:commaai/panda ca39a5d8 Added faults integer to health packet e1c34a1a Panda Jungle testing (#394) 2a093a39 Added heartbeat to echo test 22464356 Fixed health struct size. We should really get an automated test for this f458d67a Add uptime counter to the health packet (#391) 16624811 enable CAN transcievers outside the set_safety_mode function, which is not related a7c98744 bump panda ver 1192d934 Power saving refactor (#389) d58d08fb Fix Misra 17.8: can't mod function params bc685ac9 Minor indent a54b86c4 Failure of set_safety_mode falls back to SILENT. Failure to set silent results in hanging 597436d3 NOOUTPUT safety mode is now SILENT. NOOUTPUT still exists but keeps C… (#388) d229f8dc ESP forced off in EON build. this prevents ESP to be turned on when e… (#387) 8a044b34 forgot Hyundai: now also using make_msg 4f9c8796 remove abunch of lines from safety regression tests by using common make_msg function fb814143 mispelled word 57f5ef8c Fix misra: addr can't be more than 29 bits anyway 68ff5012 typo d5c772b0 Fixe Toyota message white-list 48197a92 Better masking for ELM mode b8fe78c3 VW is also tested for safety replay 212d336b Safety Chrysler: Added cancel spam button check d44b5621 fix print in example 02d579a5 functional addr handling 6249a183 tx_hook shall have a white-list of messages (#381) 8138fc14 uds: handle function addrs and fw version query example 6626a542 Fixed python health api b9b79e8b uds zero second timeout e0de1a4f define ALLOW_DEBUG in safety tests 86dec4b8 Safety modes that violate ISO26262 requirements are not compiled in RELEASE build e74ed936 safety tests a bit more simplified 2027765b relay malfunction test centralized 8af1a01a clean up safety tests e8f7a3b2 upd panda cfcce8f0 WIP: Relay malfunction (#384) 69d9d610 No tabs in mazda safety a86418c1 insignificant changes f239b996 single addr was better d063a188 Hyundai safety: re-enable button spam safety check 4d1edc06 skip tx_hook if a message is forwarded (#379) df2ff045 bump version 168461d5 added fault state to health packet b3e1a133 uds: better debug prints 68c39fb3 uds: no need for threads if you always drain rx 91b7c5bb bump Panda Ver 26cb4dc4 Fixed pylint error 32725cc3 Fixed misra compliance e33b4bea Added echo script 312ba62d minor comment cleanupo e90897a8 Fix board detection on white 0e72c183 always stop executing if safety mode fails to be set (suggested by jyoung8607) e8d7ed1d Rename function name to not confuse safety_set_mode and set_safety_mode ff86db65 improve uds message processing 512ab3f2 except Exception 37ce507a py3 all bac4d854 dos and python3 501db8d1 uds drain before send and use has_obd() f2cbec16 Added has_obd() to python library 48e5b182 Add SDK downloading to the build step (#314) e0762c2e Add Python & USB API for controlling phone power (#313) ba9fb69f New health packet struct also in the python libs git-subtree-dir: panda git-subtree-split: a648ccae4b3661ca6de7a4ac199cc44a41442b74 --- .circleci/config.yml | 2 +- Dockerfile | 2 + Jenkinsfile | 5 + README.md | 2 +- VERSION | 2 +- board/board.h | 4 +- board/board_declarations.h | 4 +- board/boards/black.h | 14 +- board/boards/common.h | 2 +- board/boards/grey.h | 13 +- board/boards/pedal.h | 11 +- board/boards/uno.h | 56 +++- board/boards/white.h | 53 +-- board/bootstub.c | 17 + board/build.mk | 4 +- board/config.h | 4 + board/critical.h | 23 ++ board/drivers/adc.h | 22 +- board/drivers/can.h | 70 ++-- board/drivers/clock.h | 24 +- board/drivers/dac.h | 17 +- board/drivers/fan.h | 29 +- board/drivers/gmlan_alt.h | 20 +- board/drivers/harness.h | 31 +- board/drivers/interrupts.h | 164 +++++++++ board/drivers/llcan.h | 36 +- board/drivers/llgpio.h | 14 +- board/drivers/pwm.h | 31 +- board/drivers/registers.h | 81 +++++ board/drivers/rtc.h | 20 +- board/drivers/spi.h | 86 ++--- board/drivers/timer.h | 6 +- board/drivers/uart.h | 17 +- board/drivers/usb.h | 156 +++++---- board/faults.h | 49 +++ board/gpio.h | 10 +- board/inc/stm32f413xx.h | 1 + board/libc.h | 25 -- board/main.c | 316 ++++++++++-------- board/main_declarations.h | 3 +- board/pedal/main.c | 26 +- board/power_saving.h | 3 + board/safety.h | 36 +- board/safety/safety_cadillac.h | 13 +- board/safety/safety_chrysler.h | 54 +-- board/safety/safety_defaults.h | 6 +- board/safety/safety_elm327.h | 2 +- board/safety/safety_ford.h | 14 +- board/safety/safety_gm.h | 48 ++- board/safety/safety_honda.h | 86 +++-- board/safety/safety_hyundai.h | 60 ++-- board/safety/safety_mazda.h | 91 ++--- board/safety/safety_subaru.h | 44 ++- board/safety/safety_toyota.h | 40 +-- board/safety/safety_volkswagen.h | 78 +++-- board/safety_declarations.h | 20 +- board/spi_flasher.h | 17 +- crypto/sign.py | 6 + drivers/linux/panda.c | 4 +- drivers/windows/panda_shared/panda.cpp | 2 +- drivers/windows/panda_shared/panda.h | 2 +- examples/can_logger.py | 9 +- examples/eps_read_software_ids.py | 59 ---- examples/query_fw_versions.py | 77 +++++ examples/tesla_tester.py | 4 +- python/__init__.py | 57 +++- python/uds.py | 188 ++++++----- requirements.txt | 4 +- tests/automated/1_program.py | 25 +- tests/automated/2_health.py | 44 +++ .../{2_usb_to_can.py => 3_usb_to_can.py} | 26 +- tests/automated/{3_wifi.py => 4_wifi.py} | 6 +- ...nctionality.py => 5_wifi_functionality.py} | 13 +- tests/automated/6_two_panda.py | 195 ----------- .../{5_wifi_udp.py => 6_wifi_udp.py} | 11 +- tests/automated/7_can_loopback.py | 202 +++++++++++ tests/automated/helpers.py | 213 +++++------- tests/automated/timeout.py | 25 ++ tests/automated/wifi_helpers.py | 85 +++++ tests/black_white_loopback_test.py | 2 +- tests/black_white_relay_endurance.py | 2 +- tests/black_white_relay_test.py | 2 +- tests/debug_console.py | 2 +- tests/development/register_hashmap_spread.py | 50 +++ tests/echo.py | 35 ++ tests/ir_test.py | 2 +- tests/misra/.gitignore | 1 + tests/misra/coverage_table | 2 +- tests/misra/test_misra.sh | 2 +- tests/safety/common.py | 36 ++ tests/safety/libpandasafety_py.py | 8 +- tests/safety/test.c | 27 +- tests/safety/test_cadillac.py | 43 +-- tests/safety/test_chrysler.py | 95 ++---- tests/safety/test_gm.py | 56 ++-- tests/safety/test_honda.py | 61 ++-- tests/safety/test_honda_bosch.py | 24 +- tests/safety/test_hyundai.py | 99 +++--- tests/safety/test_subaru.py | 43 +-- tests/safety/test_toyota.py | 93 +++--- tests/safety/test_toyota_ipas.py | 24 +- tests/safety/test_volkswagen.py | 57 ++-- tests/safety_replay/helpers.py | 6 +- tests/safety_replay/replay_drive.py | 6 +- tests/safety_replay/test_safety_replay.py | 3 +- 105 files changed, 2447 insertions(+), 1675 deletions(-) create mode 100644 board/critical.h create mode 100644 board/drivers/interrupts.h create mode 100644 board/drivers/registers.h create mode 100644 board/faults.h delete mode 100755 examples/eps_read_software_ids.py create mode 100755 examples/query_fw_versions.py create mode 100644 tests/automated/2_health.py rename tests/automated/{2_usb_to_can.py => 3_usb_to_can.py} (88%) rename tests/automated/{3_wifi.py => 4_wifi.py} (85%) rename tests/automated/{4_wifi_functionality.py => 5_wifi_functionality.py} (83%) delete mode 100644 tests/automated/6_two_panda.py rename tests/automated/{5_wifi_udp.py => 6_wifi_udp.py} (88%) create mode 100644 tests/automated/7_can_loopback.py create mode 100644 tests/automated/timeout.py create mode 100644 tests/automated/wifi_helpers.py create mode 100755 tests/development/register_hashmap_spread.py create mode 100755 tests/echo.py create mode 100644 tests/misra/.gitignore create mode 100644 tests/safety/common.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 58fc1d13bc9dbd..a4ae26efe51f35 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,7 +64,7 @@ jobs: - run: name: Build ESP image 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: machine: diff --git a/Dockerfile b/Dockerfile index a029a5ffa4af14..fa020ad0597a76 100644 --- a/Dockerfile +++ b/Dockerfile @@ -71,6 +71,8 @@ ENV PYTHONPATH /tmp:$PYTHONPATH COPY ./boardesp/get_sdk_ci.sh /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 mkdir -p /tmp/panda/boardesp/esp-open-sdk RUN chown pandauser /tmp/panda/boardesp/esp-open-sdk diff --git a/Jenkinsfile b/Jenkinsfile index 204b5beb3d925a..61068a48652c1f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,6 +14,11 @@ pipeline { steps { timeout(time: 60, unit: 'MINUTES') { 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' dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}") } diff --git a/README.md b/README.md index 634db4c120b157..ea0df2a19c246e 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ To print out the serial console from the ESP8266, run PORT=1 tests/debug_console 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. diff --git a/VERSION b/VERSION index 4279ff22f4fa77..538a65cc825174 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.5.9 \ No newline at end of file +v1.6.9 \ No newline at end of file diff --git a/board/board.h b/board/board.h index 5629a841d3f5fd..30e1fa4d17a51e 100644 --- a/board/board.h +++ b/board/board.h @@ -19,7 +19,9 @@ void detect_board_type(void) { #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))){ hw_type = HW_TYPE_WHITE_PANDA; current_board = &board_white; diff --git a/board/board_declarations.h b/board/board_declarations.h index 2fd3976a0dac68..d973551bafc2e0 100644 --- a/board/board_declarations.h +++ b/board/board_declarations.h @@ -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_esp_gps_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 uint32_t (*board_read_current)(void); typedef void (*board_set_ir_power)(uint8_t percentage); typedef void (*board_set_fan_power)(uint8_t percentage); +typedef void (*board_set_phone_power)(bool enabled); struct board { const char *board_type; @@ -27,6 +28,7 @@ struct board { board_read_current read_current; board_set_ir_power set_ir_power; board_set_fan_power set_fan_power; + board_set_phone_power set_phone_power; }; // ******************* Definitions ******************** diff --git a/board/boards/black.h b/board/boards/black.h index f033e82b23ed34..7165aa6cbb85b2 100644 --- a/board/boards/black.h +++ b/board/boards/black.h @@ -123,8 +123,8 @@ void black_set_can_mode(uint8_t mode){ } } -void black_usb_power_mode_tick(uint64_t tcnt){ - UNUSED(tcnt); +void black_usb_power_mode_tick(uint32_t uptime){ + UNUSED(uptime); // Not applicable } @@ -146,6 +146,10 @@ void black_set_fan_power(uint8_t percentage){ UNUSED(percentage); } +void black_set_phone_power(bool enabled){ + UNUSED(enabled); +} + void black_init(void) { common_init_gpio(); @@ -158,6 +162,9 @@ void black_init(void) { set_gpio_mode(GPIOC, 0, 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) // C11: OBD_SBU2_RELAY (harness relay driving output) set_gpio_mode(GPIOC, 10, MODE_OUTPUT); @@ -227,5 +234,6 @@ const board board_black = { .check_ignition = black_check_ignition, .read_current = black_read_current, .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 }; diff --git a/board/boards/common.h b/board/boards/common.h index e33b2a2f0426c4..d0a49087632ee6 100644 --- a/board/boards/common.h +++ b/board/boards/common.h @@ -61,7 +61,7 @@ void peripherals_init(void){ RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // pedal and fan PWM RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // gmlan_alt and IR PWM //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->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; diff --git a/board/boards/grey.h b/board/boards/grey.h index 1a39bce07fad9b..3dcb730a582e06 100644 --- a/board/boards/grey.h +++ b/board/boards/grey.h @@ -3,10 +3,18 @@ // ////////// // // 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 = { .board_type = "Grey", .harness_config = &white_harness_config, - .init = white_init, + .init = grey_init, .enable_can_transciever = white_enable_can_transciever, .enable_can_transcievers = white_enable_can_transcievers, .set_led = white_set_led, @@ -17,5 +25,6 @@ const board board_grey = { .check_ignition = white_check_ignition, .read_current = white_read_current, .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 }; \ No newline at end of file diff --git a/board/boards/pedal.h b/board/boards/pedal.h index 02612d3f0966e7..c67d39151d8b87 100644 --- a/board/boards/pedal.h +++ b/board/boards/pedal.h @@ -50,8 +50,8 @@ void pedal_set_can_mode(uint8_t mode){ } } -void pedal_usb_power_mode_tick(uint64_t tcnt){ - UNUSED(tcnt); +void pedal_usb_power_mode_tick(uint32_t uptime){ + UNUSED(uptime); // Not applicable } @@ -73,6 +73,10 @@ void pedal_set_fan_power(uint8_t percentage){ UNUSED(percentage); } +void pedal_set_phone_power(bool enabled){ + UNUSED(enabled); +} + void pedal_init(void) { common_init_gpio(); @@ -108,5 +112,6 @@ const board board_pedal = { .check_ignition = pedal_check_ignition, .read_current = pedal_read_current, .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 }; \ No newline at end of file diff --git a/board/boards/uno.h b/board/boards/uno.h index f5765c226f8a6f..1545a3f2dbcddf 100644 --- a/board/boards/uno.h +++ b/board/boards/uno.h @@ -1,6 +1,8 @@ // ///////////// // // Uno + Harness // // ///////////// // +#define BOOTKICK_TIME 3U +uint8_t bootkick_timer = 0U; void uno_enable_can_transciever(uint8_t transciever, bool enabled) { switch (transciever){ @@ -48,9 +50,38 @@ void uno_set_gps_load_switch(bool 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) { - UNUSED(mode); - puts("Setting USB mode makes no sense on UNO\n"); + bool valid = false; + 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) { @@ -106,12 +137,11 @@ void uno_set_can_mode(uint8_t mode){ } } -void uno_set_bootkick(bool enabled){ - set_gpio_output(GPIOB, 14, !enabled); -} - -void uno_usb_power_mode_tick(uint64_t tcnt){ - if(tcnt == 3U){ +void uno_usb_power_mode_tick(uint32_t uptime){ + UNUSED(uptime); + if(bootkick_timer != 0U){ + bootkick_timer--; + } else { uno_set_bootkick(false); } } @@ -152,6 +182,9 @@ void uno_init(void) { set_gpio_mode(GPIOC, 0, 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) // C11: OBD_SBU2_RELAY (harness relay driving output) set_gpio_mode(GPIOC, 10, MODE_OUTPUT); @@ -168,7 +201,7 @@ void uno_init(void) { uno_set_gps_load_switch(true); // Turn on phone regulator - set_gpio_output(GPIOB, 4, 1); + uno_set_phone_power(true); // Initialize IR PWM and set to 0% set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4); @@ -212,7 +245,7 @@ void uno_init(void) { } // Bootkick phone - uno_set_bootkick(true); + uno_bootkick(); } const harness_configuration uno_harness_config = { @@ -243,5 +276,6 @@ const board board_uno = { .check_ignition = uno_check_ignition, .read_current = uno_read_current, .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 }; diff --git a/board/boards/white.h b/board/boards/white.h index 899ba8d4fef9f5..1be0702f9496b4 100644 --- a/board/boards/white.h +++ b/board/boards/white.h @@ -78,11 +78,13 @@ void white_set_esp_gps_mode(uint8_t mode) { set_gpio_output(GPIOC, 14, 0); set_gpio_output(GPIOC, 5, 0); break; +#ifndef EON case ESP_GPS_ENABLED: // ESP ON set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 1); break; +#endif case ESP_GPS_BOOTMODE: set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 0); @@ -156,8 +158,8 @@ uint32_t white_read_current(void){ return adc_get(ADCCHAN_CURRENT); } -uint64_t marker = 0; -void white_usb_power_mode_tick(uint64_t tcnt){ +uint32_t marker = 0; +void white_usb_power_mode_tick(uint32_t uptime){ // on EON or BOOTSTUB, no state machine #if !defined(BOOTSTUB) && !defined(EON) @@ -171,47 +173,47 @@ void white_usb_power_mode_tick(uint64_t tcnt){ switch (usb_power_mode) { case USB_POWER_CLIENT: - if ((tcnt - marker) >= CLICKS) { + if ((uptime - marker) >= CLICKS) { if (!is_enumerated) { puts("USBP: didn't enumerate, switching to CDP mode\n"); // switch to CDP white_set_usb_power_mode(USB_POWER_CDP); - marker = tcnt; + marker = uptime; } } // keep resetting the timer if it's enumerated if (is_enumerated) { - marker = tcnt; + marker = uptime; } break; case USB_POWER_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 if (!is_enumerated && (current < CURRENT_THRESHOLD)) { puts("USBP: no enumeration with current draw, switching to DCP mode\n"); white_set_usb_power_mode(USB_POWER_DCP); - marker = tcnt; + marker = uptime; } } // keep resetting the timer if there's no current draw in CDP if (current >= CURRENT_THRESHOLD) { - marker = tcnt; + marker = uptime; } break; case USB_POWER_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 (current >= CURRENT_THRESHOLD) { puts("USBP: no current draw, switching back to CDP mode\n"); white_set_usb_power_mode(USB_POWER_CDP); - marker = tcnt; + marker = uptime; } } // keep resetting the timer if there's current draw in DCP if (current < CURRENT_THRESHOLD) { - marker = tcnt; + marker = uptime; } break; default: @@ -219,7 +221,7 @@ void white_usb_power_mode_tick(uint64_t tcnt){ break; } #else - UNUSED(tcnt); + UNUSED(uptime); #endif } @@ -236,7 +238,11 @@ bool white_check_ignition(void){ 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(); // C3: current sense @@ -296,13 +302,6 @@ void white_init(void) { // Set normal CAN mode 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 uint32_t voltage = adc_get_voltage(); // 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 = { .has_harness = false }; @@ -332,5 +342,6 @@ const board board_white = { .check_ignition = white_check_ignition, .read_current = white_read_current, .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 }; diff --git a/board/bootstub.c b/board/bootstub.c index 8ada20c7383224..1521b5324030d0 100644 --- a/board/bootstub.c +++ b/board/bootstub.c @@ -1,5 +1,8 @@ #define BOOTSTUB +#define VERS_TAG 0x53524556 +#define MIN_VERSION 2 + #include "config.h" #include "obj/gitversion.h" @@ -29,7 +32,11 @@ const board *current_board; // ********************* Includes ********************* #include "libc.h" #include "provision.h" +#include "critical.h" +#include "faults.h" +#include "drivers/registers.h" +#include "drivers/interrupts.h" #include "drivers/clock.h" #include "drivers/llgpio.h" #include "drivers/adc.h" @@ -65,6 +72,9 @@ extern void *_app_start[]; // BOUNTY: $200 coupon on shop.comma.ai or $100 check. int main(void) { + // Init interrupt table + init_interrupts(true); + disable_interrupts(); clock_init(); detect_configuration(); @@ -83,6 +93,13 @@ int main(void) { uint8_t digest[SHA_DIGEST_SIZE]; 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 if (RSA_verify(&release_rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) { goto good; diff --git a/board/build.mk b/board/build.mk index acac03095b334d..21daf53ad02710 100644 --- a/board/build.mk +++ b/board/build.mk @@ -33,7 +33,7 @@ POSTCOMPILE = @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@ # this no longer pushes the bootstub 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 curl http://192.168.0.10/stupdate --upload-file $< @@ -42,7 +42,7 @@ bin: obj/$(PROJ_NAME).bin # this flashes everything 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 $(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 diff --git a/board/config.h b/board/config.h index c2eb412e960903..15096b69fa809b 100644 --- a/board/config.h +++ b/board/config.h @@ -5,6 +5,7 @@ //#define DEBUG_UART //#define DEBUG_USB //#define DEBUG_SPI +//#define DEBUG_FAULTS #ifdef STM32F4 #define PANDA @@ -37,5 +38,8 @@ #define MAX_RESP_LEN 0x40U +// Around (1Mbps / 8 bits/byte / 12 bytes per message) +#define CAN_INTERRUPT_RATE 12000U + #endif diff --git a/board/critical.h b/board/critical.h new file mode 100644 index 00000000000000..c8cf52c7a11533 --- /dev/null +++ b/board/critical.h @@ -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(); \ + } diff --git a/board/drivers/adc.h b/board/drivers/adc.h index 2a91fef8dcd64e..358497adbfdcda 100644 --- a/board/drivers/adc.h +++ b/board/drivers/adc.h @@ -9,26 +9,16 @@ #define ADCCHAN_CURRENT 13 void adc_init(void) { - // global setup - ADC->CCR = ADC_CCR_TSVREFE | ADC_CCR_VBATE; - //ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EOCS | ADC_CR2_DDS; - 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; + register_set(&(ADC->CCR), ADC_CCR_TSVREFE | ADC_CCR_VBATE, 0xC30000U); + register_set(&(ADC1->CR2), ADC_CR2_ADON, 0xFF7F0F03U); + register_set(&(ADC1->SMPR1), ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13, 0x7FFFFFFU); } uint32_t adc_get(unsigned int channel) { - // includes length - //ADC1->SQR1 = 0; - - // select channel - ADC1->JSQR = channel << 15; - - //ADC1->CR1 = ADC_CR1_DISCNUM_0; - //ADC1->CR1 = ADC_CR1_EOCIE; + // Select channel + register_set(&(ADC1->JSQR), (channel << 15U), 0x3FFFFFU); + // Start conversion ADC1->SR &= ~(ADC_SR_JEOC); ADC1->CR2 |= ADC_CR2_JSWSTART; while (!(ADC1->SR & ADC_SR_JEOC)); diff --git a/board/drivers/can.h b/board/drivers/can.h index c9bf2d25436b35..07895e3e08d2d1 100644 --- a/board/drivers/can.h +++ b/board/drivers/can.h @@ -27,11 +27,12 @@ void can_set_forwarding(int from, int to); void can_init(uint8_t can_number); 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); // Ignition detected from CAN meessages bool ignition_can = false; +uint32_t ignition_can_cnt = 0U; // 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) { for (uint8_t i=0U; i < CAN_MAX; i++) { can_init(i); } - current_board->enable_can_transcievers(true); } 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) { - int bus = GET_BUS(to_push); int addr = GET_ADDR(to_push); int len = GET_LEN(to_push); + ignition_can_cnt = 0U; // reset counter + if (bus == 0) { // GM exception 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 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.RDLR = to_push.RDLR; 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); @@ -408,20 +392,20 @@ void can_rx(uint8_t can_number) { } } -void CAN1_TX_IRQHandler(void) { process_can(0); } -void CAN1_RX0_IRQHandler(void) { can_rx(0); } -void CAN1_SCE_IRQHandler(void) { can_sce(CAN1); } +void CAN1_TX_IRQ_Handler(void) { process_can(0); } +void CAN1_RX0_IRQ_Handler(void) { can_rx(0); } +void CAN1_SCE_IRQ_Handler(void) { can_sce(CAN1); } -void CAN2_TX_IRQHandler(void) { process_can(1); } -void CAN2_RX0_IRQHandler(void) { can_rx(1); } -void CAN2_SCE_IRQHandler(void) { can_sce(CAN2); } +void CAN2_TX_IRQ_Handler(void) { process_can(1); } +void CAN2_RX0_IRQ_Handler(void) { can_rx(1); } +void CAN2_SCE_IRQ_Handler(void) { can_sce(CAN2); } -void CAN3_TX_IRQHandler(void) { process_can(2); } -void CAN3_RX0_IRQHandler(void) { can_rx(2); } -void CAN3_SCE_IRQHandler(void) { can_sce(CAN3); } +void CAN3_TX_IRQ_Handler(void) { process_can(2); } +void CAN3_RX0_IRQ_Handler(void) { can_rx(2); } +void CAN3_SCE_IRQ_Handler(void) { can_sce(CAN3); } -void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number) { - if (safety_tx_hook(to_push) != 0) { +void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number, bool skip_tx_hook) { + if (skip_tx_hook || safety_tx_hook(to_push) != 0) { if (bus_number < BUS_MAX) { // add CAN packet to send queue // bus number isn't passed through @@ -440,3 +424,25 @@ void can_set_forwarding(int from, int 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); + } +} + diff --git a/board/drivers/clock.h b/board/drivers/clock.h index d564c7f01db28d..b75692438ec63a 100644 --- a/board/drivers/clock.h +++ b/board/drivers/clock.h @@ -1,25 +1,24 @@ void clock_init(void) { // enable external oscillator - RCC->CR |= RCC_CR_HSEON; + register_set_bits(&(RCC->CR), RCC_CR_HSEON); while ((RCC->CR & RCC_CR_HSERDY) == 0); // 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 - RCC->PLLCFGR = RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLM_3 | - RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLSRC_HSE; + 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); // start PLL - RCC->CR |= RCC_CR_PLLON; + register_set_bits(&(RCC->CR), RCC_CR_PLLON); while ((RCC->CR & RCC_CR_PLLRDY) == 0); // Configure Flash prefetch, Instruction cache, Data cache and wait state // *** 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 - 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); // *** running on PLL *** @@ -27,14 +26,15 @@ void clock_init(void) { void watchdog_init(void) { // setup watchdog - IWDG->KR = 0x5555; - IWDG->PR = 0; // divider /4 + IWDG->KR = 0x5555U; + register_set(&(IWDG->PR), 0x0U, 0x7U); // divider/4 + // 0 = 0.125 ms, let's have a 50ms watchdog - IWDG->RLR = 400 - 1; - IWDG->KR = 0xCCCC; + register_set(&(IWDG->RLR), (400U-1U), 0xFFFU); + IWDG->KR = 0xCCCCU; } void watchdog_feed(void) { - IWDG->KR = 0xAAAA; + IWDG->KR = 0xAAAAU; } diff --git a/board/drivers/dac.h b/board/drivers/dac.h index ac565eb221cd05..4bdb16100c7bb6 100644 --- a/board/drivers/dac.h +++ b/board/drivers/dac.h @@ -2,22 +2,19 @@ void puth(unsigned int i); void puts(const char *a); void dac_init(void) { - // no buffers required since we have an opamp - //DAC->CR = DAC_CR_EN1 | DAC_CR_BOFF1 | DAC_CR_EN2 | DAC_CR_BOFF2; - DAC->DHR12R1 = 0; - DAC->DHR12R2 = 0; - DAC->CR = DAC_CR_EN1 | DAC_CR_EN2; + // No buffers required since we have an opamp + register_set(&(DAC->DHR12R1), 0U, 0xFFFU); + register_set(&(DAC->DHR12R2), 0U, 0xFFFU); + register_set(&(DAC->CR), DAC_CR_EN1 | DAC_CR_EN2, 0x3FFF3FFFU); } void dac_set(int channel, uint32_t value) { if (channel == 0) { - DAC->DHR12R1 = value; + register_set(&(DAC->DHR12R1), value, 0xFFFU); } else if (channel == 1) { - DAC->DHR12R2 = value; + register_set(&(DAC->DHR12R2), value, 0xFFFU); } else { - puts("Failed to set DAC: invalid channel value: "); - puth(value); - puts("\n"); + puts("Failed to set DAC: invalid channel value: 0x"); puth(value); puts("\n"); } } diff --git a/board/drivers/fan.h b/board/drivers/fan.h index d7326ec0b33d59..2f10e5ca8d7b51 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -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){ pwm_set(TIM3, 3, percentage); } @@ -27,10 +15,25 @@ void fan_tick(void){ } // TACH interrupt handler -void EXTI2_IRQHandler(void) { +void EXTI2_IRQ_Handler(void) { volatile unsigned int pr = EXTI->PR & (1U << 2); if ((pr & (1U << 2)) != 0U) { fan_tach_counter++; } 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); } \ No newline at end of file diff --git a/board/drivers/gmlan_alt.h b/board/drivers/gmlan_alt.h index c697a21b41ef9c..6d4ba12b8c1069 100644 --- a/board/drivers/gmlan_alt.h +++ b/board/drivers/gmlan_alt.h @@ -124,15 +124,15 @@ int get_bit_message(char *out, CAN_FIFOMailBox_TypeDef *to_bang) { void setup_timer4(void) { // setup - TIM4->PSC = 48-1; // tick on 1 us - TIM4->CR1 = TIM_CR1_CEN; // enable - TIM4->ARR = 30-1; // 33.3 kbps + register_set(&(TIM4->PSC), (48-1), 0xFFFFU); // Tick on 1 us + register_set(&(TIM4->CR1), TIM_CR1_CEN, 0x3FU); // Enable + register_set(&(TIM4->ARR), (30-1), 0xFFFFU); // 33.3 kbps // in case it's disabled NVIC_EnableIRQ(TIM4_IRQn); // run the interrupt - TIM4->DIER = TIM_DIER_UIE; // update interrupt + register_set(&(TIM4->DIER), TIM_DIER_UIE, 0x5F5FU); // Update interrupt TIM4->SR = 0; } @@ -171,9 +171,9 @@ void reset_gmlan_switch_timeout(void) { void set_bitbanged_gmlan(int val) { if (val != 0) { - GPIOB->ODR |= (1U << 13); + register_set_bits(&(GPIOB->ODR), (1U << 13)); } 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 MAX_FAIL_COUNT 10 -void TIM4_IRQHandler(void) { +void TIM4_IRQ_Handler(void) { if (gmlan_alt_mode == BITBANG) { if ((TIM4->SR & TIM_SR_UIF) && (gmlan_sendmax != -1)) { 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)) { set_bitbanged_gmlan(1); // recessive set_gpio_mode(GPIOB, 13, MODE_INPUT); - TIM4->DIER = 0; // no update interrupt - TIM4->CR1 = 0; // disable timer + register_clear_bits(&(TIM4->DIER), TIM_DIER_UIE); // No update interrupt + register_set(&(TIM4->CR1), 0U, 0x3FU); // Disable timer gmlan_sendmax = -1; // exit } } @@ -279,6 +279,8 @@ bool bitbang_gmlan(CAN_FIFOMailBox_TypeDef *to_bang) { set_bitbanged_gmlan(1); // recessive set_gpio_mode(GPIOB, 13, MODE_OUTPUT); + // 33kbps + REGISTER_INTERRUPT(TIM4_IRQn, TIM4_IRQ_Handler, 40000U, FAULT_INTERRUPT_RATE_GMLAN) setup_timer4(); } return gmlan_send_ok; diff --git a/board/drivers/harness.h b/board/drivers/harness.h index 17520a4bab8055..c996bff0b2efe7 100644 --- a/board/drivers/harness.h +++ b/board/drivers/harness.h @@ -8,7 +8,7 @@ uint8_t car_harness_status = 0U; struct harness_configuration { const bool has_harness; - GPIO_TypeDef *GPIO_SBU1; + GPIO_TypeDef *GPIO_SBU1; GPIO_TypeDef *GPIO_SBU2; GPIO_TypeDef *GPIO_relay_normal; GPIO_TypeDef *GPIO_relay_flipped; @@ -52,28 +52,6 @@ bool harness_check_ignition(void) { 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 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); } else { set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT); - } + } // keep busses connected by default set_intercept_relay(false); - - // setup ignition interrupts - harness_setup_ignition_interrupts(); } else { puts("failed to detect car harness!\n"); } -} \ No newline at end of file +} diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h new file mode 100644 index 00000000000000..f15c441ab04dfe --- /dev/null +++ b/board/drivers/interrupts.h @@ -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; iSR = 0; +} + +void init_interrupts(bool check_rate_limit){ + check_interrupt_rate = check_rate_limit; + + for(uint16_t i=0U; iAPB1ENR), 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 \ No newline at end of file diff --git a/board/drivers/llcan.h b/board/drivers/llcan.h index 0a698d4e8d9b93..4cd9b4b5abd416 100644 --- a/board/drivers/llcan.h +++ b/board/drivers/llcan.h @@ -19,25 +19,24 @@ void puts(const char *a); bool llcan_set_speed(CAN_TypeDef *CAN_obj, uint32_t speed, bool loopback, bool silent) { // 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); // 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_speed_to_prescaler(speed) - 1U); + (can_speed_to_prescaler(speed) - 1U)), 0xC37F03FFU); // silent loopback mode for debugging 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) { - CAN_obj->BTR |= CAN_BTR_SILM; + register_set_bits(&(CAN_obj->BTR), CAN_BTR_SILM); } // reset - // cppcheck-suppress redundantAssignment ; it's a register - CAN_obj->MCR = CAN_MCR_TTCM | CAN_MCR_ABOM; + register_set(&(CAN_obj->MCR), CAN_MCR_TTCM | CAN_MCR_ABOM, 0x180FFU); #define CAN_TIMEOUT 1000000 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) { - // accept all filter - CAN_obj->FMR |= CAN_FMR_FINIT; + // Enter init mode + 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 - CAN_obj->sFilterRegister[0].FR1 = 0; - CAN_obj->sFilterRegister[0].FR2 = 0; - CAN_obj->sFilterRegister[14].FR1 = 0; - CAN_obj->sFilterRegister[14].FR2 = 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].FR1 = 0U; + CAN_obj->sFilterRegister[0].FR2 = 0U; + CAN_obj->sFilterRegister[14].FR1 = 0U; + CAN_obj->sFilterRegister[14].FR2 = 0U; 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 - 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) { NVIC_EnableIRQ(CAN1_TX_IRQn); @@ -87,7 +91,7 @@ void llcan_init(CAN_TypeDef *CAN_obj) { void llcan_clear_send(CAN_TypeDef *CAN_obj) { 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 CAN_obj->MSR = CAN_obj->MSR; } diff --git a/board/drivers/llgpio.h b/board/drivers/llgpio.h index 9304cbe0105e22..0bd58c3b8a5316 100644 --- a/board/drivers/llgpio.h +++ b/board/drivers/llgpio.h @@ -15,16 +15,16 @@ void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { uint32_t tmp = GPIO->MODER; tmp &= ~(3U << (pin * 2U)); tmp |= (mode << (pin * 2U)); - GPIO->MODER = tmp; + register_set(&(GPIO->MODER), tmp, 0xFFFFFFFFU); EXIT_CRITICAL(); } void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { ENTER_CRITICAL(); if (enabled) { - GPIO->ODR |= (1U << pin); + register_set_bits(&(GPIO->ODR), (1U << pin)); } else { - GPIO->ODR &= ~(1U << pin); + register_clear_bits(&(GPIO->ODR), (1U << pin)); } set_gpio_mode(GPIO, pin, MODE_OUTPUT); 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){ ENTER_CRITICAL(); if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { - GPIO->OTYPER |= (1U << pin); + register_set_bits(&(GPIO->OTYPER), (1U << pin)); } else { - GPIO->OTYPER &= ~(1U << pin); + register_clear_bits(&(GPIO->OTYPER), (1U << pin)); } 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]; tmp &= ~(0xFU << ((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); EXIT_CRITICAL(); } @@ -55,7 +55,7 @@ void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { uint32_t tmp = GPIO->PUPDR; tmp &= ~(3U << (pin * 2U)); tmp |= (mode << (pin * 2U)); - GPIO->PUPDR = tmp; + register_set(&(GPIO->PUPDR), tmp, 0xFFFFFFFFU); EXIT_CRITICAL(); } diff --git a/board/drivers/pwm.h b/board/drivers/pwm.h index d2e1652c1ce4ff..c3709200c1ce1e 100644 --- a/board/drivers/pwm.h +++ b/board/drivers/pwm.h @@ -1,53 +1,54 @@ #define PWM_COUNTER_OVERFLOW 2000U // To get ~50kHz +// TODO: Implement for 32-bit timers + void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ // 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 switch(channel){ case 1U: - TIM->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE); - TIM->CCER |= TIM_CCER_CC1E; + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); break; case 2U: - TIM->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE); - TIM->CCER |= TIM_CCER_CC2E; + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); break; case 3U: - TIM->CCMR2 |= (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE); - TIM->CCER |= TIM_CCER_CC3E; + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); break; case 4U: - TIM->CCMR2 |= (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE); - TIM->CCER |= TIM_CCER_CC4E; + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); break; default: break; } // Set max counter value - TIM->ARR = PWM_COUNTER_OVERFLOW; + register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); // Update registers and clear counter TIM->EGR |= TIM_EGR_UG; } -// TODO: Implement for 32-bit timers void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); switch(channel){ case 1U: - TIM->CCR1 = comp_value; + register_set(&(TIM->CCR1), comp_value, 0xFFFFU); break; case 2U: - TIM->CCR2 = comp_value; + register_set(&(TIM->CCR2), comp_value, 0xFFFFU); break; case 3U: - TIM->CCR3 = comp_value; + register_set(&(TIM->CCR3), comp_value, 0xFFFFU); break; case 4U: - TIM->CCR4 = comp_value; + register_set(&(TIM->CCR4), comp_value, 0xFFFFU); break; default: break; diff --git a/board/drivers/registers.h b/board/drivers/registers.h new file mode 100644 index 00000000000000..76748295d9f7be --- /dev/null +++ b/board/drivers/registers.h @@ -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; iBDCR & RCC_BDCR_MASK) != RCC_BDCR_OPTIONS){ puts("Initializing RTC\n"); // Reset backup domain - RCC->BDCR |= RCC_BDCR_BDRST; + register_set_bits(&(RCC->BDCR), RCC_BDCR_BDRST); // Disable write protection - PWR->CR |= PWR_CR_DBP; + register_set_bits(&(PWR->CR), PWR_CR_DBP); // Clear backup domain reset - RCC->BDCR &= ~(RCC_BDCR_BDRST); + register_clear_bits(&(RCC->BDCR), RCC_BDCR_BDRST); // 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 - 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"); // Disable write protection - PWR->CR |= PWR_CR_DBP; + register_set_bits(&(PWR->CR), PWR_CR_DBP); RTC->WPR = 0xCA; RTC->WPR = 0x53; // Enable initialization mode - RTC->ISR |= RTC_ISR_INIT; + register_set_bits(&(RTC->ISR), RTC_ISR_INIT); while((RTC->ISR & RTC_ISR_INITF) == 0){} // 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); // Set options - RTC->CR = 0U; + register_set(&(RTC->CR), 0U, 0xFCFFFFU); // Disable initalization mode - RTC->ISR &= ~(RTC_ISR_INIT); + register_clear_bits(&(RTC->ISR), RTC_ISR_INIT); // Wait for synchronization while((RTC->ISR & RTC_ISR_RSF) == 0){} // Re-enable write protection RTC->WPR = 0x00; - PWR->CR &= ~(PWR_CR_DBP); + register_clear_bits(&(PWR->CR), PWR_CR_DBP); } } diff --git a/board/drivers/spi.h b/board/drivers/spi.h index 26690984de7bb8..29963b6bdfce77 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -10,45 +10,22 @@ uint8_t spi_buf[SPI_BUF_SIZE]; int spi_buf_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) { // disable DMA - SPI1->CR2 &= ~SPI_CR2_TXDMAEN; - DMA2_Stream3->CR &= ~DMA_SxCR_EN; + register_clear_bits(&(SPI1->CR2), SPI_CR2_TXDMAEN); + register_clear_bits(&(DMA2_Stream3->CR), DMA_SxCR_EN); // 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->PAR = (uint32_t)&(SPI1->DR); + register_set(&(DMA2_Stream3->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU); // 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); - 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 // 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) { // disable DMA - SPI1->CR2 &= ~SPI_CR2_RXDMAEN; - DMA2_Stream2->CR &= ~DMA_SxCR_EN; + register_clear_bits(&(SPI1->CR2), SPI_CR2_RXDMAEN); + register_clear_bits(&(DMA2_Stream2->CR), DMA_SxCR_EN); // drain the bus volatile uint8_t dat = SPI1->DR; (void)dat; // 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->PAR = (uint32_t)&(SPI1->DR); + register_set(&(DMA2_Stream2->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU); // 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); - 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 ***************************** - // can't go on the stack cause it's DMAed uint8_t spi_tx_buf[0x44]; // SPI RX -void DMA2_Stream2_IRQHandler(void) { +void DMA2_Stream2_IRQ_Handler(void) { int *resp_len = (int*)spi_tx_buf; (void)memset(spi_tx_buf, 0xaa, 0x44); *resp_len = spi_cb_rx(spi_buf, 0x14, spi_tx_buf+4); @@ -99,7 +75,7 @@ void DMA2_Stream2_IRQHandler(void) { } // SPI TX -void DMA2_Stream3_IRQHandler(void) { +void DMA2_Stream3_IRQ_Handler(void) { #ifdef DEBUG_SPI puts("SPI handshake\n"); #endif @@ -112,7 +88,7 @@ void DMA2_Stream3_IRQHandler(void) { DMA2->LIFCR = DMA_LIFCR_CTCIF3; } -void EXTI4_IRQHandler(void) { +void EXTI4_IRQ_Handler(void) { volatile unsigned int pr = EXTI->PR & (1U << 4); #ifdef DEBUG_SPI puts("exti4\n"); @@ -125,3 +101,31 @@ void EXTI4_IRQHandler(void) { 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); +} \ No newline at end of file diff --git a/board/drivers/timer.h b/board/drivers/timer.h index a14b619e4b81e3..d7aa7e8811093a 100644 --- a/board/drivers/timer.h +++ b/board/drivers/timer.h @@ -1,7 +1,7 @@ void timer_init(TIM_TypeDef *TIM, int psc) { - TIM->PSC = psc-1; - TIM->DIER = TIM_DIER_UIE; - TIM->CR1 = TIM_CR1_CEN; + register_set(&(TIM->PSC), (psc-1), 0xFFFFU); + register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); + register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); TIM->SR = 0; } diff --git a/board/drivers/uart.h b/board/drivers/uart.h index 3a095f67261d8e..da45803001f84f 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -197,12 +197,12 @@ void uart_interrupt_handler(uart_ring *q) { EXIT_CRITICAL(); } -void USART1_IRQHandler(void) { uart_interrupt_handler(&uart_ring_esp_gps); } -void USART2_IRQHandler(void) { uart_interrupt_handler(&uart_ring_debug); } -void USART3_IRQHandler(void) { uart_interrupt_handler(&uart_ring_lin2); } -void UART5_IRQHandler(void) { uart_interrupt_handler(&uart_ring_lin1); } +void USART1_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_esp_gps); } +void USART2_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_debug); } +void USART3_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin2); } +void UART5_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin1); } -void DMA2_Stream5_IRQHandler(void) { +void DMA2_Stream5_IRQ_Handler(void) { ENTER_CRITICAL(); // Handle errors @@ -272,6 +272,13 @@ void uart_set_baud(USART_TypeDef *u, unsigned 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 uart_set_baud(q->uart, baud); q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; diff --git a/board/drivers/usb.h b/board/drivers/usb.h index 88455c71ca0b3a..a970194ffd284e 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -281,15 +281,8 @@ uint8_t binary_object_store_desc[] = { // BOS header BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType - 0x40, 0x00, // wTotalLength (LSB, MSB) - 0x03, // bNumDeviceCaps (USB 2.0 + 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 + 0x39, 0x00, // wTotalLength (LSB, MSB) + 0x02, // bNumDeviceCaps (WebUSB + WinUSB) // ------------------------------------------------- // 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 ***************************** @@ -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); } -void OTG_FS_IRQHandler(void) { +void OTG_FS_IRQ_Handler(void) { NVIC_DisableIRQ(OTG_FS_IRQn); //__disable_irq(); usb_irqhandler(); @@ -1016,3 +940,77 @@ void OTG_FS_IRQHandler(void) { 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); +} \ No newline at end of file diff --git a/board/faults.h b/board/faults.h new file mode 100644 index 00000000000000..aa5db341b5c21b --- /dev/null +++ b/board/faults.h @@ -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"); + } +} \ No newline at end of file diff --git a/board/gpio.h b/board/gpio.h index f33196e8fa37da..2b937eced4cfdd 100644 --- a/board/gpio.h +++ b/board/gpio.h @@ -23,6 +23,9 @@ void early(void) { // Reset global critical depth global_critical_depth = 0; + // Init register and interrupt tables + init_registers(); + // neccesary for DFU flashing on a non-power cycled white panda enable_interrupts(); @@ -59,13 +62,6 @@ void early(void) { detect_configuration(); 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) { #ifdef PANDA current_board->set_esp_gps_mode(ESP_GPS_DISABLED); diff --git a/board/inc/stm32f413xx.h b/board/inc/stm32f413xx.h index 0fd9f4c387d405..0962a8def1a3a6 100644 --- a/board/inc/stm32f413xx.h +++ b/board/inc/stm32f413xx.h @@ -141,6 +141,7 @@ typedef enum TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare global interrupt */ DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + FSMC_IRQn = 48, /*!< FSMC global Interrupt */ SDIO_IRQn = 49, /*!< SDIO global Interrupt */ TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ diff --git a/board/libc.h b/board/libc.h index f8d9dbce2acdda..67411fbb313391 100644 --- a/board/libc.h +++ b/board/libc.h @@ -40,28 +40,3 @@ int memcmp(const void * ptr1, const void * ptr2, unsigned int num) { 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(); \ - } - diff --git a/board/main.c b/board/main.c index 3bcae876e203d0..dd4edb33d1e6c7 100644 --- a/board/main.c +++ b/board/main.c @@ -5,10 +5,15 @@ #include "config.h" #include "obj/gitversion.h" +#include "main_declarations.h" +#include "critical.h" + #include "libc.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/llgpio.h" @@ -34,6 +39,28 @@ #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 ********************* bool check_started(void) { @@ -45,11 +72,14 @@ void debug_ring_callback(uart_ring *ring) { while (getc(ring, &rcv)) { (void)putc(ring, rcv); // misra-c2012-17.7: cast to void is ok: debug function - // jump to DFU flash - if (rcv == 'z') { - enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; - NVIC_SystemReset(); - } + // only allow bootloader entry on debug builds + #ifdef ALLOW_DEBUG + // jump to DFU flash + if (rcv == 'z') { + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + NVIC_SystemReset(); + } + #endif // normal reset 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 ****************************** // this is the only way to leave silent mode 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) { - puts("Error: safety set mode failed\n"); - } else { - switch (mode) { - case SAFETY_NOOUTPUT: - set_intercept_relay(false); - if(board_has_obd()){ - current_board->set_can_mode(CAN_MODE_NORMAL); - } - can_silent = ALL_CAN_SILENT; - 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; + puts("Error: safety set mode failed. Falling back to SILENT\n"); + mode_copy = SAFETY_SILENT; + err = set_safety_hooks(mode_copy, 0); + if (err == -1) { + puts("Error: Failed setting SILENT mode. Hanging\n"); + while (true) { + // TERMINAL ERROR: we can't continue if SILENT safety mode isn't succesfully set + } + } + } + switch (mode_copy) { + case SAFETY_SILENT: + set_intercept_relay(false); + if (board_has_obd()) { + current_board->set_can_mode(CAN_MODE_NORMAL); } - 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 ***************************** int get_health_pkt(void *dat) { - struct __attribute__((packed)) { - 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; - 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; + COMPILE_TIME_ASSERT(sizeof(struct health_t) <= MAX_RESP_LEN); + struct health_t * health = (struct health_t*)dat; + health->uptime_pkt = uptime_cnt; health->voltage_pkt = adc_get_voltage(); 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->usb_power_mode_pkt = usb_power_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); } @@ -230,7 +229,7 @@ void usb_cb_ep3_out(void *usbdata, int len, bool hardwired) { to_push.RIR = d32[dpkt]; 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_len = 2; break; + // **** 0xb3: set phone power + case 0xb3: + current_board->set_phone_power(setup->b.wValue.w > 0U); + break; // **** 0xc0: get CAN debug info case 0xc0: 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: resp_len = get_health_pkt(resp); 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 case 0xd6: 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 case 0xdc: // Blocked over WiFi. - // Allow NOOUTPUT and ELM security mode to be set over wifi. - if (hardwired || (setup->b.wValue.w == SAFETY_NOOUTPUT) || (setup->b.wValue.w == SAFETY_ELM327)) { + // Allow SILENT, NOOUTPUT and ELM security mode to be set over wifi. + 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); } break; @@ -526,6 +549,10 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) case 0xe6: current_board->set_usb_power_mode(setup->b.wValue.w); 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 case 0xf0: if (setup->b.wValue.w == 1U) { @@ -640,24 +667,21 @@ void __attribute__ ((noinline)) enable_fpu(void) { SCB->CPACR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U))); } -uint64_t tcnt = 0; - -// go into NOOUTPUT when the EON does not send a heartbeat for this amount of seconds. +// go into SILENT 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_OFF 2U // called once per second -// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck -void TIM1_BRK_TIM9_IRQHandler(void) { +void TIM1_BRK_TIM9_IRQ_Handler(void) { if (TIM9->SR != 0) { 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"); // reset this every 16th pass - if ((tcnt & 0xFU) == 0U) { + if ((uptime_cnt & 0xFU) == 0U) { pending_can_live = 0; } #ifdef DEBUG @@ -676,30 +700,57 @@ void TIM1_BRK_TIM9_IRQHandler(void) { // turn off the blue LED, turned on by CAN // 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 if (heartbeat_counter < __UINT32_MAX__) { 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 + // 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)) { - puts("EON hasn't sent a heartbeat for 0x"); puth(heartbeat_counter); puts(" seconds. Safety is set to NOOUTPUT mode.\n"); - if(current_safety_mode != SAFETY_NOOUTPUT){ - set_safety_mode(SAFETY_NOOUTPUT, 0U); + puts("EON hasn't sent a heartbeat for 0x"); + puth(heartbeat_counter); + 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 + // 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 - tcnt += 1U; + uptime_cnt += 1U; + safety_mode_cnt += 1U; + ignition_can_cnt += 1U; } TIM9->SR = 0; } 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 disable_interrupts(); @@ -760,32 +811,16 @@ int main(void) { TIM2->EGR = TIM_EGR_UG; // use TIM2->CNT to read - // default to silent mode to prevent issues with Ford - // hardcode a specific safety mode if you want to force the panda to be in a specific mode - int err = safety_set_mode(SAFETY_NOOUTPUT, 0); - if (err == -1) { - puts("Failed to set safety mode\n"); - while (true) { - // if SAFETY_NOOUTPUT isn't succesfully set, we can't continue - } - } - can_silent = ALL_CAN_SILENT; - can_init_all(); + // init to SILENT and can silent + set_safety_mode(SAFETY_SILENT, 0); + + // enable CAN TXs + current_board->enable_can_transcievers(true); #ifndef EON spi_init(); #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 timer_init(TIM9, 1464); NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn); @@ -804,19 +839,30 @@ int main(void) { for (cnt=0;;cnt++) { if (power_save_status == POWER_SAVE_STATUS_DISABLED) { - int div_mode = ((usb_power_mode == USB_POWER_DCP) ? 4 : 1); - - // useful for debugging, fade breaks = panda is overloaded - for (int div_mode_loop = 0; div_mode_loop < div_mode; div_mode_loop++) { - for (int fade = 0; fade < 1024; fade += 8) { - for (int i = 0; i < (128/div_mode); i++) { - 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 + if(fault_status == FAULT_STATUS_NONE){ + #endif + int div_mode = ((usb_power_mode == USB_POWER_DCP) ? 4 : 1); + + // useful for debugging, fade breaks = panda is overloaded + for (int div_mode_loop = 0; div_mode_loop < div_mode; div_mode_loop++) { + for (int fade = 0; fade < 1024; fade += 8) { + for (int i = 0; i < (128/div_mode); i++) { + 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 { __WFI(); } diff --git a/board/main_declarations.h b/board/main_declarations.h index 8929e9ac0e0cd9..363a992dbfe8a7 100644 --- a/board/main_declarations.h +++ b/board/main_declarations.h @@ -11,4 +11,5 @@ void can_set_obd(uint8_t harness_orientation, bool obd); uint8_t hw_type = 0; const board *current_board; bool is_enumerated = 0; -uint32_t heartbeat_counter = 0; \ No newline at end of file +uint32_t heartbeat_counter = 0; +uint32_t uptime_cnt = 0; diff --git a/board/pedal/main.c b/board/pedal/main.c index e7642f0c005635..667d27bf0f8d29 100644 --- a/board/pedal/main.c +++ b/board/pedal/main.c @@ -3,7 +3,11 @@ #include "libc.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/llgpio.h" #include "drivers/adc.h" @@ -129,8 +133,7 @@ uint8_t pedal_checksum(uint8_t *dat, int len) { #define CAN_GAS_SIZE 6 #define COUNTER_CYCLE 0xFU -// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck -void CAN1_TX_IRQHandler(void) { +void CAN1_TX_IRQ_Handler(void) { // clear interrupt CAN->TSR |= CAN_TSR_RQCP0; } @@ -152,8 +155,7 @@ uint32_t current_index = 0; #define FAULT_INVALID 6U uint8_t state = FAULT_STARTUP; -// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck -void CAN1_RX0_IRQHandler(void) { +void CAN1_RX0_IRQ_Handler(void) { while ((CAN->RF0R & CAN_RF0R_FMP0) != 0) { #ifdef DEBUG 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_IRQHandler(void) { +void CAN1_SCE_IRQ_Handler(void) { state = FAULT_SCE; llcan_clear_send(CAN); } @@ -228,8 +229,7 @@ unsigned int pkt_idx = 0; int led_value = 0; -// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck -void TIM3_IRQHandler(void) { +void TIM3_IRQ_Handler(void) { #ifdef DEBUG puth(TIM3->CNT); puts(" "); @@ -296,6 +296,16 @@ void pedal(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(); // init devices diff --git a/board/power_saving.h b/board/power_saving.h index d397884f6e6a8f..3ee2170d71cf6a 100644 --- a/board/power_saving.h +++ b/board/power_saving.h @@ -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_ENABLED 1 diff --git a/board/safety.h b/board/safety.h index c68eda2c4a4864..0a216e5b1f6204 100644 --- a/board/safety.h +++ b/board/safety.h @@ -18,7 +18,7 @@ #include "safety/safety_elm327.h" // from cereal.car.CarParams.SafetyModel -#define SAFETY_NOOUTPUT 0U +#define SAFETY_SILENT 0U #define SAFETY_HONDA 1U #define SAFETY_TOYOTA 2U #define SAFETY_ELM327 3U @@ -35,8 +35,9 @@ #define SAFETY_TOYOTA_IPAS 16U #define SAFETY_ALLOUTPUT 17U #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; 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); } +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 { uint16_t id; const safety_hooks *hooks; } safety_hook_config; const safety_hook_config safety_hook_registry[] = { - {SAFETY_NOOUTPUT, &nooutput_hooks}, + {SAFETY_SILENT, &nooutput_hooks}, {SAFETY_HONDA, &honda_hooks}, {SAFETY_TOYOTA, &toyota_hooks}, {SAFETY_ELM327, &elm327_hooks}, {SAFETY_GM, &gm_hooks}, {SAFETY_HONDA_BOSCH, &honda_bosch_hooks}, - {SAFETY_FORD, &ford_hooks}, - {SAFETY_CADILLAC, &cadillac_hooks}, {SAFETY_HYUNDAI, &hyundai_hooks}, {SAFETY_CHRYSLER, &chrysler_hooks}, - {SAFETY_TESLA, &tesla_hooks}, {SAFETY_SUBARU, &subaru_hooks}, {SAFETY_MAZDA, &mazda_hooks}, {SAFETY_VOLKSWAGEN, &volkswagen_hooks}, + {SAFETY_NOOUTPUT, &nooutput_hooks}, +#ifdef ALLOW_DEBUG + {SAFETY_CADILLAC, &cadillac_hooks}, {SAFETY_TOYOTA_IPAS, &toyota_ipas_hooks}, + {SAFETY_TESLA, &tesla_hooks}, {SAFETY_ALLOUTPUT, &alloutput_hooks}, {SAFETY_GM_ASCM, &gm_ascm_hooks}, + {SAFETY_FORD, &ford_hooks}, +#endif }; -int safety_set_mode(uint16_t mode, int16_t param) { - int set_status = -1; // not set +int set_safety_hooks(uint16_t mode, int16_t param) { + 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); for (int i = 0; i < hook_config_count; i++) { if (safety_hook_registry[i].id == mode) { current_hooks = safety_hook_registry[i].hooks; current_safety_mode = safety_hook_registry[i].id; - set_status = 0; // set + set_status = 0; // set break; } } @@ -205,7 +221,7 @@ float interpolate(struct lookup_t xy, float x) { float y0 = xy.y[i]; float dx = xy.x[i+1] - x0; 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.) { dx = 0.0001; } diff --git a/board/safety/safety_cadillac.h b/board/safety/safety_cadillac.h index 4ae2045505d8a7..ccfa78d3f76b8b 100644 --- a/board/safety/safety_cadillac.h +++ b/board/safety/safety_cadillac.h @@ -1,5 +1,6 @@ #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 // real time torque limit to prevent controls spamming // 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) { int tx = 1; 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 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; } -static void cadillac_init(int16_t param) { - UNUSED(param); - controls_allowed = 0; -} - const safety_hooks cadillac_hooks = { - .init = cadillac_init, + .init = nooutput_init, .rx = cadillac_rx_hook, .tx = cadillac_tx_hook, .tx_lin = nooutput_tx_lin_hook, diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h index 9b8fc361546f59..ce9f65f2afc536 100644 --- a/board/safety/safety_chrysler.h +++ b/board/safety/safety_chrysler.h @@ -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_DOWN = 3; 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_desired_torque_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; } - // check if stock camera ECU is still online - if ((bus == 0) && (addr == 0x292)) { - chrysler_camera_detected = 1; - controls_allowed = 0; + // check if stock camera ECU is on bus 0 + if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && (addr == 0x292)) { + relay_malfunction = true; } } static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { 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 (chrysler_camera_detected) { + if (!addr_allowed(addr, bus, CHRYSLER_TX_MSGS, sizeof(CHRYSLER_TX_MSGS) / sizeof(CHRYSLER_TX_MSGS[0]))) { tx = 0; } - int addr = GET_ADDR(to_send); + if (relay_malfunction) { + tx = 0; + } // LKA STEER 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. - // ensuring that only the cancel button press is sent when controls are off. - // This avoids unintended engagements while still allowing resume spam - // TODO: fix bug preventing the button msg to be fwd'd on bus 2 + // FORCE CANCEL: only the cancel button press is allowed + if (addr == 571) { + if (GET_BYTE(to_send, 0) != 1) { + tx = 0; + } + } - // 1 allows the message through 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) { int bus_fwd = -1; int addr = GET_ADDR(to_fwd); - // forward CAN 0 -> 2 so stock LKAS camera sees messages - if ((bus_num == 0) && !chrysler_camera_detected) { - bus_fwd = 2; - } - // forward all messages from camera except LKAS_COMMAND and LKAS_HUD - if ((bus_num == 2) && !chrysler_camera_detected && (addr != 658) && (addr != 678)) { - bus_fwd = 0; + + if (!relay_malfunction) { + // forward CAN 0 -> 2 so stock LKAS camera sees messages + if (bus_num == 0) { + bus_fwd = 2; + } + // 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; } const safety_hooks chrysler_hooks = { - .init = chrysler_init, + .init = nooutput_init, .rx = chrysler_rx_hook, .tx = chrysler_tx_hook, .tx_lin = nooutput_tx_lin_hook, diff --git a/board/safety/safety_defaults.h b/board/safety/safety_defaults.h index 2743db96fe6eff..4733438c9c4bd1 100644 --- a/board/safety/safety_defaults.h +++ b/board/safety/safety_defaults.h @@ -6,7 +6,8 @@ void default_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static void nooutput_init(int16_t param) { UNUSED(param); - controls_allowed = 0; + controls_allowed = false; + relay_malfunction = false; } 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) { UNUSED(param); - controls_allowed = 1; + controls_allowed = true; + relay_malfunction = false; } static int alloutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { diff --git a/board/safety/safety_elm327.h b/board/safety/safety_elm327.h index ce405b47675f11..df515b05d6ad8f 100644 --- a/board/safety/safety_elm327.h +++ b/board/safety/safety_elm327.h @@ -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 11 bit send addresses for ISO 15765-4 if ((addr != 0x18DB33F1) && ((addr & 0x1FFF00FF) != 0x18DA00F1) && - ((addr != 0x7DF) && ((addr & 0x7F8) != 0x7E0))) { + ((addr != 0x7DF) && ((addr & 0x1FFFFFF8) != 0x7E0))) { tx = 0; } return tx; diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h index 22fc604ff1dd9d..47c7342a448a51 100644 --- a/board/safety/safety_ford.h +++ b/board/safety/safety_ford.h @@ -14,6 +14,7 @@ bool ford_moving = false; static void ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { int addr = GET_ADDR(to_push); + int bus = GET_BUS(to_push); if (addr == 0x217) { // 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; } + + if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && (addr == 0x3CA)) { + relay_malfunction = true; + } } // 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) { int tx = 1; + int addr = GET_ADDR(to_send); + // disallow actuator commands if gas or brake (with vehicle moving) are pressed // and the the latching controls_allowed flag is True int pedal_pressed = ford_gas_prev || (ford_brake_prev && ford_moving); bool current_controls_allowed = controls_allowed && !(pedal_pressed); - int addr = GET_ADDR(to_send); + + if (relay_malfunction) { + tx = 0; + } // STEER: safety check if (addr == 0x3CA) { @@ -92,6 +102,8 @@ static int ford_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } +// TODO: keep camera on bus 2 and make a fwd_hook + const safety_hooks ford_hooks = { .init = nooutput_init, .rx = ford_rx_hook, diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h index 452f70953df640..92c32ad8086a7d 100644 --- a/board/safety/safety_gm.h +++ b/board/safety/safety_gm.h @@ -18,19 +18,21 @@ const int GM_DRIVER_TORQUE_FACTOR = 4; const int GM_MAX_GAS = 3072; const int GM_MAX_REGEN = 1404; 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_gas_prev = 0; 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_desired_torque_last = 0; uint32_t gm_ts_last = 0; struct sample_t gm_torque_driver; // last few driver torques measured 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); 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); } - // 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 if (addr == 481) { 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; } } + + // 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 @@ -113,9 +114,14 @@ static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { 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 (gm_ascm_detected) { + if (relay_malfunction) { 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); bool current_controls_allowed = controls_allowed && !pedal_pressed; - int addr = GET_ADDR(to_send); - // BRAKE: safety check if (addr == 789) { 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 if (addr == 715) { 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; } -static void gm_init(int16_t param) { - UNUSED(param); - controls_allowed = 0; -} - const safety_hooks gm_hooks = { - .init = gm_init, + .init = nooutput_init, .rx = gm_rx_hook, .tx = gm_tx_hook, .tx_lin = nooutput_tx_lin_hook, diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h index f59e288812a7cf..4cef17f79d0ed5 100644 --- a/board/safety/safety_honda.h +++ b/board/safety/safety_honda.h @@ -6,7 +6,9 @@ // accel rising edge // brake rising edge // 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 int honda_brake = 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) { honda_fwd_brake = true; } 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 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 // and the the latching controls_allowed flag is True 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) { UNUSED(param); - controls_allowed = 0; + controls_allowed = false; + relay_malfunction = false; honda_bosch_hardware = false; honda_alt_brake_msg = false; } static void honda_bosch_init(int16_t param) { - controls_allowed = 0; + controls_allowed = false; + relay_malfunction = false; honda_bosch_hardware = true; // Checking for alternate brake override from safety parameter 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 int bus_fwd = -1; - if (bus_num == 0) { - bus_fwd = 2; - } - if (bus_num == 2) { - // block stock lkas messages and stock acc messages (if OP is doing ACC) - int addr = GET_ADDR(to_fwd); - bool is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D); - bool is_acc_hud_msg = (addr == 0x30C) || (addr == 0x39F); - bool is_brake_msg = addr == 0x1FA; - bool block_fwd = is_lkas_msg || - (is_acc_hud_msg && long_controls_allowed) || - (is_brake_msg && long_controls_allowed && !honda_fwd_brake); - if (!block_fwd) { - bus_fwd = 0; + if (!relay_malfunction) { + if (bus_num == 0) { + bus_fwd = 2; + } + if (bus_num == 2) { + // block stock lkas messages and stock acc messages (if OP is doing ACC) + int addr = GET_ADDR(to_fwd); + bool is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D); + bool is_acc_hud_msg = (addr == 0x30C) || (addr == 0x39F); + bool is_brake_msg = addr == 0x1FA; + bool block_fwd = is_lkas_msg || + (is_acc_hud_msg && long_controls_allowed) || + (is_brake_msg && long_controls_allowed && !honda_fwd_brake); + if (!block_fwd) { + bus_fwd = 0; + } } } 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_car = (board_has_relay()) ? 0 : 2; // radar bus, car side - if (bus_num == bus_rdr_car) { - bus_fwd = bus_rdr_cam; - } - if (bus_num == bus_rdr_cam) { - int addr = GET_ADDR(to_fwd); - int is_lkas_msg = (addr == 0xE4) || (addr == 0x33D); - if (!is_lkas_msg) { - bus_fwd = bus_rdr_car; + if (!relay_malfunction) { + if (bus_num == bus_rdr_car) { + bus_fwd = bus_rdr_cam; + } + if (bus_num == bus_rdr_cam) { + int addr = GET_ADDR(to_fwd); + int is_lkas_msg = (addr == 0xE4) || (addr == 0x33D); + if (!is_lkas_msg) { + bus_fwd = bus_rdr_car; + } } } return bus_fwd; diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index 21796e4caaf128..b2d215f6876217 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -6,9 +6,8 @@ const int HYUNDAI_MAX_RATE_DOWN = 7; const int HYUNDAI_DRIVER_TORQUE_ALLOWANCE = 50; const int HYUNDAI_DRIVER_TORQUE_FACTOR = 2; -bool hyundai_camera_detected = 0; -bool hyundai_giraffe_switch_2 = 0; // is giraffe switch 2 high? -int hyundai_camera_bus = 0; +const AddrBus HYUNDAI_TX_MSGS[] = {{832, 0}, {1265, 0}}; + int hyundai_rt_torque_last = 0; int hyundai_desired_torque_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); } - // check if stock camera ECU is still online - if ((bus == 0) && (addr == 832)) { - hyundai_camera_detected = 1; - controls_allowed = 0; - } - - // Find out which bus the camera is on - if (addr == 832) { - hyundai_camera_bus = bus; + // check if stock camera ECU is on bus 0 + if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == 0) && (addr == 832)) { + relay_malfunction = true; } // 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; } - - // 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) { int tx = 1; 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 (hyundai_camera_detected) { + if (relay_malfunction) { 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. // ensuring that only the cancel button press is sent (VAL 4) when controls are off. // 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 && (bus == 0) { - // if ((GET_BYTES_04(to_send) & 0x7) != 4) { - // tx = 0; - // } - //} + if ((addr == 1265) && !controls_allowed) { + if ((GET_BYTES_04(to_send) & 0x7) != 4) { + tx = 0; + } + } // 1 allows the message through 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) { int bus_fwd = -1; + int addr = GET_ADDR(to_fwd); // forward cam to ccan and viceversa, except lkas cmd - if (hyundai_giraffe_switch_2) { + if (!relay_malfunction) { if (bus_num == 0) { - bus_fwd = hyundai_camera_bus; + bus_fwd = 2; } - if (bus_num == hyundai_camera_bus) { - int addr = GET_ADDR(to_fwd); - if (addr != 832) { - bus_fwd = 0; - } + if ((bus_num == 2) && (addr != 832)) { + bus_fwd = 0; } } 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 = { - .init = hyundai_init, + .init = nooutput_init, .rx = hyundai_rx_hook, .tx = hyundai_tx_hook, .tx_lin = nooutput_tx_lin_hook, diff --git a/board/safety/safety_mazda.h b/board/safety/safety_mazda.h index 60d8b2bda9b16a..f1215f23da2acb 100644 --- a/board/safety/safety_mazda.h +++ b/board/safety/safety_mazda.h @@ -30,12 +30,6 @@ uint32_t mazda_ts_last = 0; 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 -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) { int bus = GET_BUS(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; if (cruise_engaged != 0) { if (!mazda_cruise_engaged_last) { - controls_allowed = 1; + controls_allowed = 1; } } else { @@ -60,17 +54,9 @@ void mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { mazda_cruise_engaged_last = cruise_engaged; } - // we have msgs on bus MAZDA_CAM - if (bus == MAZDA_CAM) { - // the stock CAM is connected - 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; - } + // if we see wheel speed msgs on MAZDA_CAM bus then relay is closed + if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (bus == MAZDA_CAM) && (addr == MAZDA_WHEEL_SPEED)) { + relay_malfunction = true; } } @@ -79,15 +65,12 @@ static int mazda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int addr = GET_ADDR(to_send); int bus = GET_BUS(to_send); + if (relay_malfunction) { + tx = 0; + } + // Check if msg is sent on the main BUS 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 if (addr == MAZDA_LKAS) { 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) { - // *** global torque limit check *** - violation |= max_limit_check(desired_torque, MAZDA_MAX_STEER, -MAZDA_MAX_STEER); - - // *** torque rate limit check *** - int desired_torque_last = mazda_desired_torque_last; - violation |= driver_limit_check(desired_torque, desired_torque_last, &mazda_torque_driver, - MAZDA_MAX_STEER, MAZDA_MAX_RATE_UP, MAZDA_MAX_RATE_DOWN, - MAZDA_DRIVER_TORQUE_ALLOWANCE, MAZDA_DRIVER_TORQUE_FACTOR); - // used next time - mazda_desired_torque_last = desired_torque; - - // *** torque real time rate limit check *** - violation |= rt_rate_limit_check(desired_torque, mazda_rt_torque_last, MAZDA_MAX_RT_DELTA); - - // every RT_INTERVAL set the new limits - uint32_t ts_elapsed = get_ts_elapsed(ts, mazda_ts_last); - if (ts_elapsed > ((uint32_t) MAZDA_RT_INTERVAL)) { - mazda_rt_torque_last = desired_torque; - mazda_ts_last = ts; - } + // *** global torque limit check *** + violation |= max_limit_check(desired_torque, MAZDA_MAX_STEER, -MAZDA_MAX_STEER); + + // *** torque rate limit check *** + int desired_torque_last = mazda_desired_torque_last; + violation |= driver_limit_check(desired_torque, desired_torque_last, &mazda_torque_driver, + MAZDA_MAX_STEER, MAZDA_MAX_RATE_UP, MAZDA_MAX_RATE_DOWN, + MAZDA_DRIVER_TORQUE_ALLOWANCE, MAZDA_DRIVER_TORQUE_FACTOR); + // used next time + mazda_desired_torque_last = desired_torque; + + // *** torque real time rate limit check *** + violation |= rt_rate_limit_check(desired_torque, mazda_rt_torque_last, MAZDA_MAX_RT_DELTA); + + // every RT_INTERVAL set the new limits + uint32_t ts_elapsed = get_ts_elapsed(ts, mazda_ts_last); + if (ts_elapsed > ((uint32_t) MAZDA_RT_INTERVAL)) { + mazda_rt_torque_last = desired_torque; + mazda_ts_last = ts; + } } // no torque if controls is not allowed if (!controls_allowed && (desired_torque != 0)) { - violation = 1; + violation = 1; } // reset to 0 if either controls is not allowed or there's a violation if (violation || !controls_allowed) { - mazda_desired_torque_last = 0; - mazda_rt_torque_last = 0; - mazda_ts_last = ts; + mazda_desired_torque_last = 0; + mazda_rt_torque_last = 0; + mazda_ts_last = ts; } 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) { int bus_fwd = -1; - if (mazda_forward_cam && !mazda_giraffe_switch_2_on) { + if (!relay_malfunction) { int addr = GET_ADDR(to_fwd); if (bus == MAZDA_MAIN) { bus_fwd = MAZDA_CAM; } else if (bus == MAZDA_CAM) { - // drop stock CAM_LKAS and CAM_LANEINFI if OP is sending them - if (!((addr == MAZDA_LKAS) && mazda_op_lkas_detected) && - !((addr == MAZDA_LANEINFO) && mazda_op_laneinfo_detected)) { - bus_fwd = MAZDA_MAIN; + if (!(addr == MAZDA_LKAS)) { + bus_fwd = MAZDA_MAIN; } } else { diff --git a/board/safety/safety_subaru.h b/board/safety/safety_subaru.h index c09b9d6225ed19..66e5947f856dfb 100644 --- a/board/safety/safety_subaru.h +++ b/board/safety/safety_subaru.h @@ -8,6 +8,8 @@ const int SUBARU_MAX_RATE_DOWN = 70; const int SUBARU_DRIVER_TORQUE_ALLOWANCE = 60; 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_rt_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; } + + 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) { int tx = 1; 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 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) { - int bus_fwd = -1; - if (bus_num == 0) { - bus_fwd = 2; // Camera CAN - } - if (bus_num == 2) { - // 290 is LKAS for Global Platform - // 356 is LKAS for outback 2015 - // 545 is ES_Distance - // 802 is ES_LKAS - int addr = GET_ADDR(to_fwd); - int block_msg = (addr == 290) || (addr == 356) || (addr == 545) || (addr == 802); - if (!block_msg) { - bus_fwd = 0; // Main CAN + + if (!relay_malfunction) { + if (bus_num == 0) { + bus_fwd = 2; // Camera CAN + } + if (bus_num == 2) { + // 290 is LKAS for Global Platform + // 356 is LKAS for outback 2015 + // 545 is ES_Distance + // 802 is ES_LKAS + int addr = GET_ADDR(to_fwd); + int block_msg = (addr == 290) || (addr == 356) || (addr == 545) || (addr == 802); + if (!block_msg) { + bus_fwd = 0; // Main CAN + } } } - // fallback to do not forward return bus_fwd; } diff --git a/board/safety/safety_toyota.h b/board/safety/safety_toyota.h index 134cc845c97283..0576a463816f62 100644 --- a/board/safety/safety_toyota.h +++ b/board/safety/safety_toyota.h @@ -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 +// 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 int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file // 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_rt_torque_last = 0; // last desired torque for real time check uint32_t toyota_ts_last = 0; @@ -87,14 +91,9 @@ static void toyota_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { toyota_gas_prev = gas; } - // msgs are only on bus 2 if panda is connected to frc - if (bus == 2) { - toyota_camera_forwarded = 1; - } - - // 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; + // 0x2E4 is lkas cmd. If it is on bus 0, then relay is unexpectedly closed + if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && (addr == 0x2E4) && (bus == 0)) { + relay_malfunction = true; } } @@ -104,14 +103,17 @@ static int toyota_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int addr = GET_ADDR(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 if (bus == 0) { - // no IPAS in non IPAS mode - if ((addr == 0x266) || (addr == 0x167)) { - tx = 0; - } - // GAS PEDAL: safety check if (addr == 0x200) { 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; } static void toyota_init(int16_t param) { controls_allowed = 0; - toyota_giraffe_switch_1 = 0; - toyota_camera_forwarded = 0; + relay_malfunction = 0; toyota_dbc_eps_torque_factor = param; } static int toyota_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { int bus_fwd = -1; - if (toyota_camera_forwarded && !toyota_giraffe_switch_1) { + if (!relay_malfunction) { if (bus_num == 0) { bus_fwd = 2; } if (bus_num == 2) { int addr = GET_ADDR(to_fwd); // 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)); // in TSS2 the camera does ACC as well, so filter 0x343 int is_acc_msg = (addr == 0x343); diff --git a/board/safety/safety_volkswagen.h b/board/safety/safety_volkswagen.h index bb184cd91f5418..c16e559ce18b23 100644 --- a/board/safety/safety_volkswagen.h +++ b/board/safety/safety_volkswagen.h @@ -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_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 @@ -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_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 int volkswagen_rt_torque_last = 0; int volkswagen_desired_torque_last = 0; uint32_t volkswagen_ts_last = 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) { int bus = GET_BUS(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; } + + 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) { @@ -65,6 +66,14 @@ static int volkswagen_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int bus = GET_BUS(to_send); 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. if (addr == MSG_HCA_01) { 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. // 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 if ((GET_BYTE(to_send, 2) & 0x9) != 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 - switch (bus_num) { - case 0: - // Forward all traffic from J533 gateway to Extended CAN devices. - bus_fwd = 2; - break; - case 2: - 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. + if (!relay_malfunction) { + switch (bus_num) { + case 0: + // Forward all traffic from J533 gateway to Extended CAN devices. + bus_fwd = 2; + break; + case 2: + 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; - } 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; - break; + break; + } } - return bus_fwd; } const safety_hooks volkswagen_hooks = { - .init = volkswagen_init, + .init = nooutput_init, .rx = volkswagen_rx_hook, .tx = volkswagen_tx_hook, .tx_lin = nooutput_tx_lin_hook, diff --git a/board/safety_declarations.h b/board/safety_declarations.h index efa6a4e2b8744c..e192d74fb7c345 100644 --- a/board/safety_declarations.h +++ b/board/safety_declarations.h @@ -11,6 +11,11 @@ struct lookup_t { float y[3]; }; +typedef struct { + int addr; + int bus; +} AddrBus; + void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push); int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_send); 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); bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA); 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 (*rx_hook)(CAN_FIFOMailBox_TypeDef *to_push); @@ -40,13 +46,19 @@ typedef struct { fwd_hook fwd; } safety_hooks; -// This can be set by the safety hooks. -bool controls_allowed = 0; -bool gas_interceptor_detected = 0; +// This can be set by the safety hooks +bool controls_allowed = false; +bool relay_malfunction = false; +bool gas_interceptor_detected = false; int gas_interceptor_prev = 0; // 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 #define GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + ((GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2 ) / 2) diff --git a/board/spi_flasher.h b/board/spi_flasher.h index b1bba71d4623c4..fbdbab8a61e0cf 100644 --- a/board/spi_flasher.h +++ b/board/spi_flasher.h @@ -65,7 +65,12 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) // so it's blocked over wifi switch (setup->b.wValue.w) { case 0: + #ifdef ALLOW_DEBUG if (hardwired) { + #else + // no more bootstub on UNO + if (hardwired && hw_type != HW_TYPE_UNO) { + #endif puts("-> entering bootloader\n"); enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; 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_OUTPUT 0x2 -void CAN1_TX_IRQHandler(void) { +void CAN1_TX_IRQ_Handler(void) { // clear interrupt 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; } -void CAN1_RX0_IRQHandler(void) { +void CAN1_RX0_IRQ_Handler(void) { while (CAN->RF0R & CAN_RF0R_FMP0) { if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) { 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); } #endif 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"); enter_bootloader_mode = 0; diff --git a/crypto/sign.py b/crypto/sign.py index 0f1ce030815b6d..8cbe6f4195f1c6 100755 --- a/crypto/sign.py +++ b/crypto/sign.py @@ -6,6 +6,9 @@ from Crypto.PublicKey import RSA import binascii +# increment this to make new hardware not run old versions +VERSION = 2 + rsa = RSA.importKey(open(sys.argv[3]).read()) with open(sys.argv[1], "rb") as f: @@ -15,6 +18,9 @@ with open(sys.argv[2], "wb") as f: 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:] # mock signature of dat[4:] dd = hashlib.sha1(dat[4:]).digest() diff --git a/drivers/linux/panda.c b/drivers/linux/panda.c index 3d4f957f9c5083..c0aa44be1840c6 100644 --- a/drivers/linux/panda.c +++ b/drivers/linux/panda.c @@ -39,7 +39,7 @@ #define PANDA_DLC_MASK 0x0F #define SAFETY_ALLOUTPUT 17 -#define SAFETY_NOOUTPUT 0 +#define SAFETY_SILENT 0 struct panda_usb_ctx { 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, usb_sndctrlpipe(priv->priv_dev->udev, 0), 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) diff --git a/drivers/windows/panda_shared/panda.cpp b/drivers/windows/panda_shared/panda.cpp index 5f711b02aa28a1..8425ec9da20953 100644 --- a/drivers/windows/panda_shared/panda.cpp +++ b/drivers/windows/panda_shared/panda.cpp @@ -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; } -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; } diff --git a/drivers/windows/panda_shared/panda.h b/drivers/windows/panda_shared/panda.h index 8d98b08b71a405..117fc92760bfd4 100644 --- a/drivers/windows/panda_shared/panda.h +++ b/drivers/windows/panda_shared/panda.h @@ -38,7 +38,7 @@ namespace panda { typedef enum _PANDA_SAFETY_MODE : uint16_t { - SAFETY_NOOUTPUT = 0, + SAFETY_SILENT = 0, SAFETY_HONDA = 1, SAFETY_ALLOUTPUT = 17, } PANDA_SAFETY_MODE; diff --git a/examples/can_logger.py b/examples/can_logger.py index 203023dc92c106..12dcf5849111a8 100755 --- a/examples/can_logger.py +++ b/examples/can_logger.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import binascii import csv import sys from panda import Panda @@ -21,7 +20,7 @@ def can_logger(): sys.exit(0) try: - outputfile = open('output.csv', 'wb') + outputfile = open('output.csv', 'w') csvwriter = csv.writer(outputfile) #Write Header csvwriter.writerow(['Bus', 'MessageID', 'Message', 'MessageLength']) @@ -35,7 +34,7 @@ def can_logger(): can_recv = p.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: bus0_msg_cnt += 1 @@ -44,10 +43,10 @@ def can_logger(): elif src == 2: 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: - 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() if __name__ == "__main__": diff --git a/examples/eps_read_software_ids.py b/examples/eps_read_software_ids.py deleted file mode 100755 index a3292207c7e90c..00000000000000 --- a/examples/eps_read_software_ids.py +++ /dev/null @@ -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) diff --git a/examples/query_fw_versions.py b/examples/query_fw_versions.py new file mode 100755 index 00000000000000..2177335f6e51a4 --- /dev/null +++ b/examples/query_fw_versions.py @@ -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!") diff --git a/examples/tesla_tester.py b/examples/tesla_tester.py index 9a77eb4edf502a..57079f56eb3eed 100644 --- a/examples/tesla_tester.py +++ b/examples/tesla_tester.py @@ -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 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!) print("Setting Panda to output mode...") p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -37,7 +37,7 @@ def tesla_tester(): #Back to safety... 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)...") diff --git a/python/__init__.py b/python/__init__.py index 326128d3ec306a..8f4ae29181bb69 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -110,7 +110,7 @@ def close(self): class Panda(object): # matches cereal.car.CarParams.SafetyModel - SAFETY_NOOUTPUT = 0 + SAFETY_SILENT = 0 SAFETY_HONDA = 1 SAFETY_TOYOTA = 2 SAFETY_ELM327 = 3 @@ -127,6 +127,7 @@ class Panda(object): SAFETY_TOYOTA_IPAS = 16 SAFETY_ALLOUTPUT = 17 SAFETY_GM_ASCM = 18 + SAFETY_NOOUTPUT = 19 SERIAL_DEBUG = 0 SERIAL_ESP = 1 @@ -345,18 +346,25 @@ def call_control_api(self, msg): # ******************* health ******************* def health(self): - dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 24) - a = struct.unpack("IIIIIBBBB", dat) + dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 37) + a = struct.unpack("IIIIIIIBBBBBBBBB", dat) return { - "voltage": a[0], - "current": a[1], - "can_send_errs": a[2], - "can_fwd_errs": a[3], - "gmlan_send_errs": a[4], - "started": a[5], - "controls_allowed": a[6], - "gas_interceptor_detected": a[7], - "car_harness_status": a[8] + "uptime": a[0], + "voltage": a[1], + "current": a[2], + "can_send_errs": a[3], + "can_fwd_errs": a[4], + "gmlan_send_errs": a[5], + "faults": a[6], + "ignition_line": a[7], + "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 ******************* @@ -371,6 +379,17 @@ def enter_bootloader(self): def get_version(self): 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): return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40) @@ -386,6 +405,9 @@ def is_black(self): def is_uno(self): return self.get_type() == Panda.HW_TYPE_UNO + def has_obd(self): + return (self.is_uno() or self.is_black()) + def get_serial(self): dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20) hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4] @@ -400,6 +422,9 @@ def get_secret(self): def set_usb_power(self, on): 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): self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'') @@ -407,7 +432,7 @@ def esp_reset(self, bootmode=0): self._handle.controlWrite(Panda.REQUEST_OUT, 0xda, int(bootmode), 0, b'') 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'') def set_can_forwarding(self, from_bus, to_bus): @@ -623,4 +648,8 @@ def set_fan_power(self, percentage): def get_fan_rpm(self): dat = self._handle.controlRead(Panda.REQUEST_IN, 0xb2, 0, 0, 2) a = struct.unpack("H", dat) - return a[0] \ No newline at end of file + return a[0] + +# ****************** Phone ***************** + def set_phone_power(self, enabled): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xb3, int(enabled), 0, b'') diff --git a/python/uds.py b/python/uds.py index 5f5ae53bfbac01..ee9e9ea667351d 100644 --- a/python/uds.py +++ b/python/uds.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 import time import struct -from typing import NamedTuple, List +from typing import Callable, NamedTuple, Tuple, List from enum import IntEnum -from queue import Queue, Empty -from threading import Thread -from binascii import hexlify class SERVICE_TYPE(IntEnum): DIAGNOSTIC_SESSION_CONTROL = 0x10 @@ -213,6 +210,7 @@ class MessageTimeoutError(Exception): class NegativeResponseError(Exception): def __init__(self, message, service_id, error_code): + super().__init__() self.message = message self.service_id = service_id self.error_code = error_code @@ -270,20 +268,79 @@ class InvalidSubFunctioneError(Exception): 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(): - def __init__(self, can_tx_queue: Queue, can_rx_queue: Queue, timeout: float, debug: bool=False): - self.can_tx_queue = can_tx_queue - self.can_rx_queue = can_rx_queue + def __init__(self, can_client: CanClient, timeout: float=1, debug: bool=False): + self._can_client = can_client self.timeout = timeout self.debug = debug def send(self, dat: bytes) -> None: + # throw away any stale data + self._can_client.recv(drain=True) + self.tx_dat = dat self.tx_len = len(dat) self.tx_idx = 0 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() def _tx_first_frame(self) -> None: @@ -296,27 +353,25 @@ def _tx_first_frame(self) -> None: # first frame (send first 6 bytes) 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") - self.can_tx_queue.put(msg) + self._can_client.send([msg]) def recv(self) -> bytes: - self.rx_dat = b"" - self.rx_len = 0 - self.rx_idx = 0 - self.rx_done = False - + start_time = time.time() try: while True: - self._isotp_rx_next() - if self.tx_done and self.rx_done: - return self.rx_dat - except Empty: - raise MessageTimeoutError("timeout waiting for response") + for msg in self._can_client.recv(): + self._isotp_rx_next(msg) + if self.tx_done and self.rx_done: + return self.rx_dat + # 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: - if self.debug: print(f"ISO-TP: RESPONSE - {hexlify(self.rx_dat)}") - - def _isotp_rx_next(self) -> None: - rx_data = self.can_rx_queue.get(block=True, timeout=self.timeout) + if self.debug and self.rx_dat: print(f"ISO-TP: RESPONSE - 0x{bytes.hex(self.rx_dat)}") + def _isotp_rx_next(self, rx_data: bytes) -> None: # single rx_frame if rx_data[0] >> 4 == 0x0: self.rx_len = rx_data[0] & 0xFF @@ -336,9 +391,9 @@ def _isotp_rx_next(self) -> None: if self.debug: print(f"ISO-TP: TX - flow control continue") # send flow control message (send all bytes) msg = b"\x30\x00\x00".ljust(8, b"\x00") - self.can_tx_queue.put(msg) + self._can_client.send([msg]) return - + # consecutive rx frame if rx_data[0] >> 4 == 0x2: assert self.rx_done == False, "isotp - rx: consecutive frame with no active frame" @@ -361,19 +416,19 @@ def _isotp_rx_next(self) -> None: delay_ts = rx_data[2] & 0x7F # 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_sec = delay_ts / delay_div # first frame = 6 bytes, each consecutive frame = 7 bytes start = 6 + self.tx_idx * 7 count = rx_data[1] end = start + count * 7 if count > 0 else self.tx_len + tx_msgs = [] 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 - # consecutive tx frames + # consecutive tx messages 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: self.tx_done = True if self.debug: print(f"ISO-TP: TX - consecutive frame - idx={self.tx_idx} done={self.tx_done}") @@ -381,57 +436,30 @@ def _isotp_rx_next(self) -> None: # wait (do nothing until next flow control message) 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(): - def __init__(self, panda, tx_addr: int, rx_addr: int=None, bus: int=0, timeout: float=10, debug: bool=False): - self.panda = panda + def __init__(self, panda, tx_addr: int, rx_addr: int=None, bus: int=0, timeout: float=1, debug: bool=False): self.bus = bus self.tx_addr = tx_addr - if rx_addr == None: - 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.rx_addr = rx_addr if rx_addr is not None else get_rx_addr_for_tx_addr(tx_addr) self.timeout = timeout self.debug = 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() + self._can_client = CanClient(panda.can_send, panda.can_recv, self.tx_addr, self.rx_addr, self.bus, debug=self.debug) # generic uds request def _uds_request(self, service_type: SERVICE_TYPE, subfunction: int=None, data: bytes=None) -> bytes: @@ -442,7 +470,7 @@ def _uds_request(self, service_type: SERVICE_TYPE, subfunction: int=None, data: req += data # 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) while True: resp = isotp_msg.recv() @@ -453,12 +481,12 @@ def _uds_request(self, service_type: SERVICE_TYPE, subfunction: int=None, data: service_id = resp[1] if len(resp) > 1 else -1 try: service_desc = SERVICE_TYPE(service_id).name - except Exception: + except BaseException: service_desc = 'NON_STANDARD_SERVICE' error_code = resp[2] if len(resp) > 2 else -1 try: error_desc = _negative_response_codes[error_code] - except Exception: + except BaseException: error_desc = resp[3:] # wait for another message if response pending if error_code == 0x78: diff --git a/requirements.txt b/requirements.txt index e7cf9931d550e7..5ceb3cf6e7d6cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,6 @@ tqdm>=4.14.0 nose parameterized requests -flake8==3.7.8 -pylint==2.4.2 +flake8==3.7.9 +pylint==2.4.3 cffi==1.11.4 diff --git a/tests/automated/1_program.py b/tests/automated/1_program.py index 538f18c0c7f63a..98c32114fe7bb7 100644 --- a/tests/automated/1_program.py +++ b/tests/automated/1_program.py @@ -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 @panda_connect_and_init def test_recover(p): assert p.recover(timeout=30) + @test_all_pandas @panda_connect_and_init def test_flash(p): 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) diff --git a/tests/automated/2_health.py b/tests/automated/2_health.py new file mode 100644 index 00000000000000..da5eaec9d995ba --- /dev/null +++ b/tests/automated/2_health.py @@ -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)) \ No newline at end of file diff --git a/tests/automated/2_usb_to_can.py b/tests/automated/3_usb_to_can.py similarity index 88% rename from tests/automated/2_usb_to_can.py rename to tests/automated/3_usb_to_can.py index 32ef558cfc409a..282a37ef1c6be3 100644 --- a/tests/automated/2_usb_to_can.py +++ b/tests/automated/3_usb_to_can.py @@ -2,11 +2,18 @@ import time from panda import Panda 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 @panda_connect_and_init def test_can_loopback(p): + # Start heartbeat + start_heartbeat_thread(p) + # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -40,8 +47,11 @@ def test_can_loopback(p): @test_all_pandas @panda_connect_and_init def test_safety_nooutput(p): + # Start heartbeat + start_heartbeat_thread(p) + # enable output mode - p.set_safety_mode(Panda.SAFETY_NOOUTPUT) + p.set_safety_mode(Panda.SAFETY_SILENT) # enable CAN loopback mode p.set_can_loopback(True) @@ -60,6 +70,9 @@ def test_reliability(p): LOOP_COUNT = 100 MSG_COUNT = 100 + # Start heartbeat + start_heartbeat_thread(p) + # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_can_loopback(True) @@ -95,6 +108,9 @@ def test_reliability(p): @test_all_pandas @panda_connect_and_init def test_throughput(p): + # Start heartbeat + start_heartbeat_thread(p) + # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -122,6 +138,9 @@ def test_gmlan(p): if p.legacy: return + # Start heartbeat + start_heartbeat_thread(p) + # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -153,6 +172,9 @@ def test_gmlan_bad_toggle(p): if p.legacy: return + # Start heartbeat + start_heartbeat_thread(p) + # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) diff --git a/tests/automated/3_wifi.py b/tests/automated/4_wifi.py similarity index 85% rename from tests/automated/3_wifi.py rename to tests/automated/4_wifi.py index df66d6c0f37d2f..15b8dc6c9ed9a4 100644 --- a/tests/automated/3_wifi.py +++ b/tests/automated/4_wifi.py @@ -1,8 +1,12 @@ import time 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 +# Reset the pandas before running tests +def aaaa_reset_before_tests(): + reset_pandas() + @test_all_pandas @panda_connect_and_init def test_get_serial(p): diff --git a/tests/automated/4_wifi_functionality.py b/tests/automated/5_wifi_functionality.py similarity index 83% rename from tests/automated/4_wifi_functionality.py rename to tests/automated/5_wifi_functionality.py index ee349ddcbf69b8..88c81dcbdcdd75 100644 --- a/tests/automated/4_wifi_functionality.py +++ b/tests/automated/5_wifi_functionality.py @@ -1,6 +1,10 @@ import time 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 @panda_type_to_serial @@ -16,6 +20,9 @@ def test_throughput(serials=None): connect_wifi(serials[0]) p = Panda(serials[0]) + # Start heartbeat + start_heartbeat_thread(p) + # enable output mode p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -43,6 +50,10 @@ def test_throughput(serials=None): def test_recv_only(serials=None): connect_wifi(serials[0]) p = Panda(serials[0]) + + # Start heartbeat + start_heartbeat_thread(p) + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_can_loopback(True) diff --git a/tests/automated/6_two_panda.py b/tests/automated/6_two_panda.py deleted file mode 100644 index f91403545f367a..00000000000000 --- a/tests/automated/6_two_panda.py +++ /dev/null @@ -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) diff --git a/tests/automated/5_wifi_udp.py b/tests/automated/6_wifi_udp.py similarity index 88% rename from tests/automated/5_wifi_udp.py rename to tests/automated/6_wifi_udp.py index fd905aa895e4b3..197e4d46c73476 100644 --- a/tests/automated/5_wifi_udp.py +++ b/tests/automated/6_wifi_udp.py @@ -1,16 +1,23 @@ - import sys 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 nose.tools import assert_less, assert_greater +# Reset the pandas before running tests +def aaaa_reset_before_tests(): + reset_pandas() + @test_white @panda_type_to_serial def test_udp_doesnt_drop(serials=None): connect_wifi(serials[0]) p = Panda(serials[0]) + + # Start heartbeat + start_heartbeat_thread(p) + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_can_loopback(True) diff --git a/tests/automated/7_can_loopback.py b/tests/automated/7_can_loopback.py new file mode 100644 index 00000000000000..cb9f5570b5551f --- /dev/null +++ b/tests/automated/7_can_loopback.py @@ -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) diff --git a/tests/automated/helpers.py b/tests/automated/helpers.py index 2642e9156ad316..78a9287c10be7e 100644 --- a/tests/automated/helpers.py +++ b/tests/automated/helpers.py @@ -5,21 +5,52 @@ import subprocess import requests import _thread +import faulthandler from functools import wraps from panda import Panda +from panda_jungle import PandaJungle # pylint: disable=import-error from nose.tools import assert_equal from parameterized import parameterized, param +from .timeout import run_with_timeout +from .wifi_helpers import _connect_wifi SPEED_NORMAL = 500 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([ param(panda_type=Panda.HW_TYPE_WHITE_PANDA), param(panda_type=Panda.HW_TYPE_GREY_PANDA), param(panda_type=Panda.HW_TYPE_BLACK_PANDA) ]) 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([ param(panda_type=Panda.HW_TYPE_WHITE_PANDA), @@ -31,13 +62,8 @@ test_grey = parameterized([ param(panda_type=Panda.HW_TYPE_GREY_PANDA) ]) -test_two_panda = parameterized([ - param(panda_type=[Panda.HW_TYPE_GREY_PANDA, Panda.HW_TYPE_WHITE_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]) +test_black = parameterized([ + param(panda_type=Panda.HW_TYPE_BLACK_PANDA) ]) def connect_wifi(serial=None): @@ -47,111 +73,26 @@ def connect_wifi(serial=None): assert(dongle_id.isalnum()) _connect_wifi(dongle_id, pw) -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 - -def time_many_sends(p, bus, precv=None, msg_count=100, msg_id=None, two_pandas=False): - if precv == None: - precv = p +def time_many_sends(p, bus, p_recv=None, msg_count=100, msg_id=None, two_pandas=False): + if p_recv == None: + p_recv = p if msg_id == None: 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") - st = time.time() + start_time = time.time() p.can_send_many([(msg_id, 0, b"\xaa"*8, bus)]*msg_count) r = [] r_echo = [] r_len_expected = msg_count if two_pandas else msg_count*2 r_echo_len_exected = msg_count if two_pandas else 0 - while len(r) < r_len_expected and (time.time() - st) < 5: - r.extend(precv.can_recv()) - et = time.time() + while len(r) < r_len_expected and (time.time() - start_time) < 5: + r.extend(p_recv.can_recv()) + end_time = time.time() 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()) 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(sent_echo), msg_count) - et = (et-st)*1000.0 - comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7)*msg_count / et + 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 / end_time 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): @wraps(fn) def wrapper(panda_type=None, **kwargs): @@ -181,11 +127,7 @@ def wrapper(panda_type=None, **kwargs): # If not done already, get panda serials and their type global _panda_serials if _panda_serials == None: - _panda_serials = [] - for serial in Panda.list(): - p = Panda(serial=serial) - _panda_serials.append((serial, p.get_type())) - p.close() + init_panda_serials() # Find a panda with the correct types and add the corresponding serial serials = [] @@ -202,13 +144,15 @@ def wrapper(panda_type=None, **kwargs): return fn(serials, **kwargs) return wrapper -def heartbeat_thread(p): - while True: - try: - p.send_heartbeat() - time.sleep(1) - except: - break +def start_heartbeat_thread(p): + def heartbeat_thread(p): + while True: + try: + p.send_heartbeat() + time.sleep(1) + except: + break + _thread.start_new_thread(heartbeat_thread, (p,)) def panda_connect_and_init(fn): @wraps(fn) @@ -223,25 +167,38 @@ def wrapper(panda_serials=None, **kwargs): for panda_serial in panda_serials: 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 for panda in pandas: panda.set_can_loopback(False) panda.set_gmlan(None) 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) clear_can_buffers(panda) - _thread.start_new_thread(heartbeat_thread, (panda,)) - - # Run test function - ret = fn(*pandas, **kwargs) + panda.set_power_save(False) - # Close all connections - for panda in pandas: - panda.close() - - # Return test function result - return ret + try: + run_with_timeout(TIMEOUT, fn, *pandas, **kwargs) + + # Check if the pandas did not throw any faults while running test + for panda in pandas: + 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 def clear_can_buffers(panda): diff --git a/tests/automated/timeout.py b/tests/automated/timeout.py new file mode 100644 index 00000000000000..f937844f07e8b5 --- /dev/null +++ b/tests/automated/timeout.py @@ -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)) \ No newline at end of file diff --git a/tests/automated/wifi_helpers.py b/tests/automated/wifi_helpers.py new file mode 100644 index 00000000000000..de9b224a1ac351 --- /dev/null +++ b/tests/automated/wifi_helpers.py @@ -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 \ No newline at end of file diff --git a/tests/black_white_loopback_test.py b/tests/black_white_loopback_test.py index 66c5e80f452ad6..d700068d9e7d16 100755 --- a/tests/black_white_loopback_test.py +++ b/tests/black_white_loopback_test.py @@ -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) # Toggle relay - black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT) + black_panda.set_safety_mode(Panda.SAFETY_SILENT) time.sleep(1) black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) time.sleep(1) diff --git a/tests/black_white_relay_endurance.py b/tests/black_white_relay_endurance.py index 6970966526c7bc..d3d61be6616fab 100755 --- a/tests/black_white_relay_endurance.py +++ b/tests/black_white_relay_endurance.py @@ -72,7 +72,7 @@ def run_test(sleep_duration): if (time.time() - temp_start_time) > 3600*6: # Toggle relay - black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT) + black_panda.set_safety_mode(Panda.SAFETY_SILENT) time.sleep(1) black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) time.sleep(1) diff --git a/tests/black_white_relay_test.py b/tests/black_white_relay_test.py index d80490f7d465da..f12e42d39b023f 100755 --- a/tests/black_white_relay_test.py +++ b/tests/black_white_relay_test.py @@ -74,7 +74,7 @@ def run_test(sleep_duration): assert False # Switch off relay - black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT) + black_panda.set_safety_mode(Panda.SAFETY_SILENT) time.sleep(0.05) if not test_buses(black_panda, other_panda, (0, False, [0, 2])): diff --git a/tests/debug_console.py b/tests/debug_console.py index 8e66946dd4d8db..7b354b1487a080 100755 --- a/tests/debug_console.py +++ b/tests/debug_console.py @@ -44,6 +44,6 @@ if claim: panda.serial_write(port_number, ln) time.sleep(0.01) - except: + except Exception: print("panda disconnected!") time.sleep(0.5); diff --git a/tests/development/register_hashmap_spread.py b/tests/development/register_hashmap_spread.py new file mode 100755 index 00000000000000..ba0a0eb29d8fde --- /dev/null +++ b/tests/development/register_hashmap_spread.py @@ -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() diff --git a/tests/echo.py b/tests/echo.py new file mode 100755 index 00000000000000..9ef0cf1074b73f --- /dev/null +++ b/tests/echo.py @@ -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) + + diff --git a/tests/ir_test.py b/tests/ir_test.py index caef9e4909d3d9..a65fe068657650 100755 --- a/tests/ir_test.py +++ b/tests/ir_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os import sys import time diff --git a/tests/misra/.gitignore b/tests/misra/.gitignore new file mode 100644 index 00000000000000..fa3aeaa30613ce --- /dev/null +++ b/tests/misra/.gitignore @@ -0,0 +1 @@ +cppcheck/ diff --git a/tests/misra/coverage_table b/tests/misra/coverage_table index dcb5992bc74743..9dc89bd4f5d744 100644 --- a/tests/misra/coverage_table +++ b/tests/misra/coverage_table @@ -7,7 +7,7 @@ 2.4 X (Cppcheck) 2.5 2.6 X (Cppcheck) -2.7 +2.7 X (Addon) 3.1 X (Addon) 3.2 X (Addon) 4.1 X (Addon) diff --git a/tests/misra/test_misra.sh b/tests/misra/test_misra.sh index 2542290744d963..47a38dad23c12c 100755 --- a/tests/misra/test_misra.sh +++ b/tests/misra/test_misra.sh @@ -4,7 +4,7 @@ mkdir /tmp/misra || true git clone https://github.com/danmar/cppcheck.git || true cd cppcheck git fetch -git checkout bdd41151ed550e3d240a6dd6847859216b7ad736 +git checkout e46191e6e809272d8b34feca8999ee413f716b80 make -j4 cd ../../../ diff --git a/tests/safety/common.py b/tests/safety/common.py new file mode 100644 index 00000000000000..99f72379211986 --- /dev/null +++ b/tests/safety/common.py @@ -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))) diff --git a/tests/safety/libpandasafety_py.py b/tests/safety/libpandasafety_py.py index 819fcada37e3d2..5b1bd28c9ad7d9 100644 --- a/tests/safety/libpandasafety_py.py +++ b/tests/safety/libpandasafety_py.py @@ -34,6 +34,8 @@ void set_controls_allowed(bool c); bool get_controls_allowed(void); +void set_relay_malfunction(bool c); +bool get_relay_malfunction(void); void set_long_controls_allowed(bool c); bool get_long_controls_allowed(void); void set_gas_interceptor_detected(bool c); @@ -46,7 +48,7 @@ void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_send); int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_push); 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); int get_toyota_torque_meas_min(void); @@ -54,7 +56,6 @@ int get_toyota_gas_prev(void); void set_toyota_torque_meas(int min, int max); void set_toyota_desired_torque_last(int t); -void set_toyota_camera_forwarded(int t); void set_toyota_rt_torque_last(int t); void init_tests_honda(void); @@ -80,13 +81,10 @@ void set_hyundai_desired_torque_last(int t); void set_hyundai_rt_torque_last(int t); 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 set_chrysler_desired_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_max(void); void set_chrysler_torque_meas(int min, int max); diff --git a/tests/safety/test.c b/tests/safety/test.c index c61f64077c63b1..de12a4d046a802 100644 --- a/tests/safety/test.c +++ b/tests/safety/test.c @@ -42,6 +42,8 @@ TIM_TypeDef *TIM2 = &timer; #define HW_TYPE_PEDAL 4U #define HW_TYPE_UNO 5U +#define ALLOW_DEBUG + // from main_declarations.h uint8_t hw_type = HW_TYPE_UNKNOWN; @@ -80,6 +82,10 @@ void set_controls_allowed(bool c){ controls_allowed = c; } +void set_relay_malfunction(bool c){ + relay_malfunction = c; +} + void set_long_controls_allowed(bool c){ long_controls_allowed = c; } @@ -96,6 +102,10 @@ bool get_controls_allowed(void){ return controls_allowed; } +bool get_relay_malfunction(void){ + return relay_malfunction; +} + bool get_long_controls_allowed(void){ return long_controls_allowed; } @@ -116,10 +126,6 @@ void set_timer(uint32_t t){ timer.CNT = t; } -void set_toyota_camera_forwarded(int t){ - toyota_camera_forwarded = t; -} - void set_toyota_torque_meas(int min, int max){ toyota_torque_meas.min = min; toyota_torque_meas.max = max; @@ -140,18 +146,6 @@ void set_hyundai_torque_driver(int min, int 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){ chrysler_torque_meas.min = min; chrysler_torque_meas.max = max; @@ -278,6 +272,7 @@ void set_honda_fwd_brake(bool c){ void init_tests(void){ // get HW_TYPE from env variable set in test.sh hw_type = atoi(getenv("HW_TYPE")); + safety_mode_cnt = 2U; // avoid ignoring relay_malfunction logic } void init_tests_toyota(void){ diff --git a/tests/safety/test_cadillac.py b/tests/safety/test_cadillac.py index f211908b7bd11d..7806fb7b285cc1 100644 --- a/tests/safety/test_cadillac.py +++ b/tests/safety/test_cadillac.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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_DOWN = 5 @@ -16,6 +18,8 @@ IPAS_OVERRIDE_THRESHOLD = 200 +TX_MSGS = [[0x151, 2], [0x152, 0], [0x153, 2], [0x154, 0]] + def twos_comp(val, bits): if val >= 0: return val @@ -32,59 +36,42 @@ class TestCadillacSafety(unittest.TestCase): @classmethod def setUp(cls): 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() - 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): self.safety.set_cadillac_desired_torque_last(t) self.safety.set_cadillac_rt_torque_last(t) 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) + to_send = make_msg(0, 0x164) to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8) return to_send def _torque_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x151 << 21 - + to_send = make_msg(2, 0x151) t = twos_comp(torque, 14) to_send[0].RDLR = ((t >> 8) & 0x3F) | ((t & 0xFF) << 8) return to_send + def test_spam_can_buses(self): + test_spam_can_buses(self, TX_MSGS) + def test_default_controls_not_allowed(self): self.assertFalse(self.safety.get_controls_allowed()) def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_controls_allowed(0) + test_manually_enable_controls_allowed(self) def test_enable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x370 << 21 + to_push = make_msg(0, 0x370) to_push[0].RDLR = 0x800000 - to_push[0].RDTR = 0 - self.safety.safety_rx_hook(to_push) self.assertTrue(self.safety.get_controls_allowed()) def test_disable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x370 << 21 - to_push[0].RDLR = 0 - to_push[0].RDTR = 0 - + to_push = make_msg(0, 0x370) self.safety.set_controls_allowed(1) self.safety.safety_rx_hook(to_push) self.assertFalse(self.safety.get_controls_allowed()) @@ -190,7 +177,7 @@ def test_fwd_hook(self): for b in buss: for m in msgs: # 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__": diff --git a/tests/safety/test_chrysler.py b/tests/safety/test_chrysler.py index 37bffe0eaf392b..2c472706fe1c99 100755 --- a/tests/safety/test_chrysler.py +++ b/tests/safety/test_chrysler.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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_DOWN = 3 @@ -13,40 +14,17 @@ MAX_TORQUE_ERROR = 80 -def twos_comp(val, bits): - 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) +TX_MSGS = [[571, 0], [658, 0], [678, 0]] class TestChryslerSafety(unittest.TestCase): @classmethod def setUp(cls): 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() - 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): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 1265 << 21 + to_send = make_msg(0, 571) to_send[0].RDLR = buttons return to_send @@ -56,17 +34,21 @@ def _set_prev_torque(self, t): self.safety.set_chrysler_torque_meas(t, t) def _torque_meas_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 544 << 21 + to_send = make_msg(0, 544) to_send[0].RDHR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8) return to_send def _torque_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x292 << 21 + to_send = make_msg(0, 0x292) to_send[0].RDLR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8) 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): self.assertFalse(self.safety.get_controls_allowed()) @@ -81,22 +63,17 @@ def test_steer_safety_check(self): self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_controls_allowed(0) - self.assertFalse(self.safety.get_controls_allowed()) + test_manually_enable_controls_allowed(self) def test_enable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x1f4 << 21 + to_push = make_msg(0, 0x1F4) to_push[0].RDLR = 0x380000 self.safety.safety_rx_hook(to_push) self.assertTrue(self.safety.get_controls_allowed()) def test_disable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x1f4 << 21 + to_push = make_msg(0, 0x1F4) to_push[0].RDLR = 0 self.safety.set_controls_allowed(1) @@ -179,28 +156,30 @@ def test_torque_measurements(self): self.assertEqual(0, self.safety.get_chrysler_torque_meas_max()) 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): buss = list(range(0x0, 0x3)) msgs = list(range(0x1, 0x800)) - chrysler_camera_detected = [0, 1] - - for ccd in chrysler_camera_detected: - self.safety.set_chrysler_camera_detected(ccd) - blocked_msgs = [658, 678] - for b in buss: - for m in msgs: - if not ccd: - if b == 0: - fwd_bus = 2 - elif b == 1: - fwd_bus = -1 - 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))) + + blocked_msgs = [658, 678] + for b in buss: + for m in msgs: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8))) if __name__ == "__main__": diff --git a/tests/safety/test_gm.py b/tests/safety/test_gm.py index 304dbbe939bf48..99c15750c46fd5 100644 --- a/tests/safety/test_gm.py +++ b/tests/safety/test_gm.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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_DOWN = 17 @@ -17,6 +18,11 @@ DRIVER_TORQUE_ALLOWANCE = 50; 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): if val >= 0: return val @@ -33,50 +39,37 @@ class TestGmSafety(unittest.TestCase): @classmethod def setUp(cls): 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() - 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): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 842 << 21 + to_send = make_msg(0, 842) to_send[0].RDLR = speed return to_send def _button_msg(self, buttons): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 481 << 21 + to_send = make_msg(0, 481) to_send[0].RDHR = buttons << 12 return to_send def _brake_msg(self, brake): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 241 << 21 + to_send = make_msg(0, 241) to_send[0].RDLR = 0xa00 if brake else 0x900 return to_send def _gas_msg(self, gas): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 417 << 21 + to_send = make_msg(0, 417) to_send[0].RDHR = (1 << 16) if gas else 0 return to_send def _send_brake_msg(self, brake): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 789 << 21 + to_send = make_msg(2, 789) brake = (-brake) & 0xfff to_send[0].RDLR = (brake >> 8) | ((brake &0xff) << 8) return to_send def _send_gas_msg(self, gas): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 715 << 21 + to_send = make_msg(0, 715) to_send[0].RDLR = ((gas & 0x1f) << 27) | ((gas & 0xfe0) << 11) return to_send @@ -85,21 +78,23 @@ def _set_prev_torque(self, t): self.safety.set_gm_rt_torque_last(t) 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) + to_send = make_msg(0, 388) to_send[0].RDHR = (((t >> 8) & 0x7) << 16) | ((t & 0xFF) << 24) return to_send 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) + to_send = make_msg(0, 384) to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8) 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): self.assertFalse(self.safety.get_controls_allowed()) @@ -198,10 +193,7 @@ def test_steer_safety_check(self): self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_controls_allowed(0) - self.assertFalse(self.safety.get_controls_allowed()) + test_manually_enable_controls_allowed(self) def test_non_realtime_limit_up(self): self.safety.set_gm_torque_driver(0, 0) @@ -289,7 +281,7 @@ def test_fwd_hook(self): for b in buss: for m in msgs: # 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__": diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py index 55a376a2e58a37..6ab247835af7e4 100755 --- a/tests/safety/test_honda.py +++ b/tests/safety/test_honda.py @@ -1,94 +1,79 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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 INTERCEPTOR_THRESHOLD = 328 +TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x200, 0], [0x30C, 0], [0x33D, 0], [0x39F, 0]] class TestHondaSafety(unittest.TestCase): @classmethod def setUp(cls): 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() - 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): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x158 << 21 + to_send = make_msg(0, 0x158) to_send[0].RDLR = speed - return to_send 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() honda_bosch_hardware = self.safety.get_honda_bosch_hardware() 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 def _brake_msg(self, brake): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x17C << 21 + to_send = make_msg(0, 0x17C) to_send[0].RDHR = 0x200000 if brake else 0 - return to_send def _alt_brake_msg(self, brake): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x1BE << 21 + to_send = make_msg(0, 0x1BE) to_send[0].RDLR = 0x10 if brake else 0 - return to_send def _gas_msg(self, gas): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x17C << 21 + to_send = make_msg(0, 0x17C) to_send[0].RDLR = 1 if gas else 0 - return to_send def _send_brake_msg(self, brake): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x1FA << 21 + to_send = make_msg(0, 0x1FA) to_send[0].RDLR = ((brake & 0x3) << 14) | ((brake & 0x3FF) >> 2) - return to_send 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 + to_send = make_msg(0, addr, 6) gas2 = gas * 2 to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \ ((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8) - return to_send def _send_steer_msg(self, steer): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0xE4 << 21 + to_send = make_msg(0, 0xE4, 6) to_send[0].RDLR = steer - 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): 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): RESUME_BTN = 4 self.safety.set_controls_allowed(0) @@ -281,7 +266,7 @@ def test_fwd_hook(self): fwd_bus = -1 if m in blocked_msgs else 0 # 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_honda_fwd_brake(False) diff --git a/tests/safety/test_honda_bosch.py b/tests/safety/test_honda_bosch.py index 7571e1eddc4576..eed3622bcda9f2 100755 --- a/tests/safety/test_honda_bosch.py +++ b/tests/safety/test_honda_bosch.py @@ -1,29 +1,31 @@ #!/usr/bin/env python3 import unittest -import libpandasafety_py # pylint: disable=import-error 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 +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): @classmethod def setUp(cls): 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() - 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 test_spam_can_buses(self): + if self.safety.board_has_relay(): + test_spam_can_buses(self, H_TX_MSGS) + else: + test_spam_can_buses(self, G_TX_MSGS) def test_fwd_hook(self): buss = range(0x0, 0x3) msgs = range(0x1, 0x800) - #has_relay = self.safety.get_hw_type() == 3 # black panda has_relay = self.safety.board_has_relay() bus_rdr_cam = 2 if has_relay else 1 bus_rdr_car = 0 if has_relay else 2 @@ -40,7 +42,7 @@ def test_fwd_hook(self): fwd_bus = bus_rdr_cam # 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__": diff --git a/tests/safety/test_hyundai.py b/tests/safety/test_hyundai.py index d8fa02691b6d41..9067ec9842870a 100644 --- a/tests/safety/test_hyundai.py +++ b/tests/safety/test_hyundai.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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_DOWN = 7 @@ -14,6 +15,8 @@ DRIVER_TORQUE_ALLOWANCE = 50; DRIVER_TORQUE_FACTOR = 2; +TX_MSGS = [[832, 0], [1265, 0]] + def twos_comp(val, bits): if val >= 0: return val @@ -30,19 +33,11 @@ class TestHyundaiSafety(unittest.TestCase): @classmethod def setUp(cls): 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() - 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): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 1265 << 21 + to_send = make_msg(0, 1265) to_send[0].RDLR = buttons return to_send @@ -51,17 +46,21 @@ def _set_prev_torque(self, t): self.safety.set_hyundai_rt_torque_last(t) def _torque_driver_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 897 << 21 + to_send = make_msg(0, 897) to_send[0].RDLR = (torque + 2048) << 11 return to_send def _torque_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 832 << 21 + to_send = make_msg(0, 832) to_send[0].RDLR = (torque + 1024) << 16 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): self.assertFalse(self.safety.get_controls_allowed()) @@ -76,24 +75,16 @@ def test_steer_safety_check(self): self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_controls_allowed(0) - self.assertFalse(self.safety.get_controls_allowed()) + test_manually_enable_controls_allowed(self) def test_enable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 1057 << 21 + to_push = make_msg(0, 1057) to_push[0].RDLR = 1 << 13 - self.safety.safety_rx_hook(to_push) self.assertTrue(self.safety.get_controls_allowed()) def test_disable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 1057 << 21 - to_push[0].RDLR = 0 - + to_push = make_msg(0, 1057) self.safety.set_controls_allowed(1) self.safety.safety_rx_hook(to_push) self.assertFalse(self.safety.get_controls_allowed()) @@ -176,43 +167,35 @@ def test_realtime_limits(self): self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) - #def test_spam_cancel_safety_check(self): - # RESUME_BTN = 1 - # SET_BTN = 2 - # CANCEL_BTN = 4 - # BUTTON_MSG = 1265 - # self.safety.set_controls_allowed(0) - # 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(SET_BTN))) - # # do not block resume if we are engaged already - # self.safety.set_controls_allowed(1) - # self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN))) + def test_spam_cancel_safety_check(self): + RESUME_BTN = 1 + SET_BTN = 2 + CANCEL_BTN = 4 + self.safety.set_controls_allowed(0) + 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(SET_BTN))) + # do not block resume if we are engaged already + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN))) def test_fwd_hook(self): buss = list(range(0x0, 0x3)) msgs = list(range(0x1, 0x800)) - hyundai_giraffe_switch_2 = [0, 1] - - self.safety.set_hyundai_camera_bus(2) - for hgs in hyundai_giraffe_switch_2: - self.safety.set_hyundai_giraffe_switch_2(hgs) - blocked_msgs = [832] - for b in buss: - for m in msgs: - if hgs: - if b == 0: - fwd_bus = 2 - elif b == 1: - fwd_bus = -1 - 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))) + + blocked_msgs = [832] + for b in buss: + for m in msgs: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8))) if __name__ == "__main__": diff --git a/tests/safety/test_subaru.py b/tests/safety/test_subaru.py index 49933e6636dd3c..e18d7515cb1345 100644 --- a/tests/safety/test_subaru.py +++ b/tests/safety/test_subaru.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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_DOWN = 70 @@ -14,6 +15,8 @@ DRIVER_TORQUE_ALLOWANCE = 60; DRIVER_TORQUE_FACTOR = 10; +TX_MSGS = [[0x122, 0], [0x164, 0], [0x221, 0], [0x322, 0]] + def twos_comp(val, bits): if val >= 0: return val @@ -30,52 +33,43 @@ class TestSubaruSafety(unittest.TestCase): @classmethod def setUp(cls): 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() - 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): self.safety.set_subaru_desired_torque_last(t) self.safety.set_subaru_rt_torque_last(t) 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) + to_send = make_msg(0, 0x119) to_send[0].RDLR = ((t & 0x7FF) << 16) return to_send def _torque_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x122 << 21 - + to_send = make_msg(0, 0x122) t = twos_comp(torque, 13) to_send[0].RDLR = (t << 16) 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): self.assertFalse(self.safety.get_controls_allowed()) def test_enable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x240 << 21 + to_push = make_msg(0, 0x240) to_push[0].RDHR = 1 << 9 - self.safety.safety_rx_hook(to_push) self.assertTrue(self.safety.get_controls_allowed()) def test_disable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x240 << 21 + to_push = make_msg(0, 0x240) to_push[0].RDHR = 0 - self.safety.set_controls_allowed(1) self.safety.safety_rx_hook(to_push) self.assertFalse(self.safety.get_controls_allowed()) @@ -91,10 +85,7 @@ def test_steer_safety_check(self): self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_controls_allowed(0) - self.assertFalse(self.safety.get_controls_allowed()) + test_manually_enable_controls_allowed(self) def test_non_realtime_limit_up(self): self.safety.set_subaru_torque_driver(0, 0) @@ -188,7 +179,7 @@ def test_fwd_hook(self): fwd_bus = -1 if m in blocked_msgs else 0 # 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__": diff --git a/tests/safety/test_toyota.py b/tests/safety/test_toyota.py index 15d3b20cc5f8bc..58e32f036e50b0 100644 --- a/tests/safety/test_toyota.py +++ b/tests/safety/test_toyota.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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_DOWN = 25 @@ -15,9 +16,14 @@ RT_INTERVAL = 250000 MAX_TORQUE_ERROR = 350 - 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): if val >= 0: return val @@ -34,75 +40,60 @@ class TestToyotaSafety(unittest.TestCase): @classmethod def setUp(cls): 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() - 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): self.safety.set_toyota_desired_torque_last(t) self.safety.set_toyota_rt_torque_last(t) self.safety.set_toyota_torque_meas(t, t) 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) + to_send = make_msg(0, 0x260) to_send[0].RDHR = t | ((t & 0xFF) << 16) return to_send 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) + to_send = make_msg(0, 0x2E4) to_send[0].RDLR = t | ((t & 0xFF) << 16) return to_send def _accel_msg(self, accel): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x343 << 21 - + to_send = make_msg(0, 0x343) a = twos_comp(accel, 16) to_send[0].RDLR = (a & 0xFF) << 8 | (a >> 8) return to_send def _send_gas_msg(self, gas): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x2C1 << 21 + to_send = make_msg(0, 0x2C1) to_send[0].RDHR = (gas & 0xFF) << 16 - return to_send 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 + to_send = make_msg(0, addr, 6) to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \ ((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8) - return to_send def _pcm_cruise_msg(self, cruise_on): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x1D2 << 21 + to_send = make_msg(0, 0x1D2) to_send[0].RDLR = cruise_on << 5 - 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): self.assertFalse(self.safety.get_controls_allowed()) def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) + test_manually_enable_controls_allowed(self) def test_enable_control_allowed_from_cruise(self): self.safety.safety_rx_hook(self._pcm_cruise_msg(False)) @@ -285,29 +276,23 @@ def test_fwd_hook(self): buss = list(range(0x0, 0x3)) msgs = list(range(0x1, 0x800)) long_controls_allowed = [0, 1] - toyota_camera_forwarded = [0, 1] - - for tcf in toyota_camera_forwarded: - self.safety.set_toyota_camera_forwarded(tcf) - for lca in long_controls_allowed: - self.safety.set_long_controls_allowed(lca) - blocked_msgs = [0x2E4, 0x412, 0x191] - if lca: - blocked_msgs += [0x343] - for b in buss: - for m in msgs: - if tcf: - if b == 0: - fwd_bus = 2 - elif b == 1: - fwd_bus = -1 - 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))) + + for lca in long_controls_allowed: + self.safety.set_long_controls_allowed(lca) + blocked_msgs = [0x2E4, 0x412, 0x191] + if lca: + blocked_msgs += [0x343] + for b in buss: + for m in msgs: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8))) self.safety.set_long_controls_allowed(True) diff --git a/tests/safety/test_toyota_ipas.py b/tests/safety/test_toyota_ipas.py index df0f36ffd76e47..680fc149b7e55c 100644 --- a/tests/safety/test_toyota_ipas.py +++ b/tests/safety/test_toyota_ipas.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error from panda import Panda +from panda.tests.safety import libpandasafety_py +from panda.tests. safety.common import make_msg IPAS_OVERRIDE_THRESHOLD = 200 @@ -26,13 +27,11 @@ class TestToyotaSafety(unittest.TestCase): @classmethod def setUp(cls): cls.safety = libpandasafety_py.libpandasafety - cls.safety.safety_set_mode(Panda.SAFETY_TOYOTA_IPAS, 66) + cls.safety.set_safety_hooks(Panda.SAFETY_TOYOTA_IPAS, 66) cls.safety.init_tests_toyota() def _torque_driver_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x260 << 21 - + to_send = make_msg(0, 0x260) t = twos_comp(torque, 16) to_send[0].RDLR = t | ((t & 0xFF) << 16) return to_send @@ -42,9 +41,7 @@ def _torque_driver_msg_array(self, torque): self.safety.safety_rx_hook(self._torque_driver_msg(torque)) def _angle_meas_msg(self, angle): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x25 << 21 - + to_send = make_msg(0, 0x25) t = twos_comp(angle, 12) to_send[0].RDLR = ((t & 0xF00) >> 8) | ((t & 0xFF) << 8) return to_send @@ -54,17 +51,13 @@ def _angle_meas_msg_array(self, angle): self.safety.safety_rx_hook(self._angle_meas_msg(angle)) def _ipas_state_msg(self, state): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x262 << 21 - + to_send = make_msg(0, 0x262) to_send[0].RDLR = state & 0xF return to_send def _ipas_control_msg(self, angle, state): # note: we command 2/3 of the angle due to CAN conversion - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x266 << 21 - + to_send = make_msg(0, 0x266) t = twos_comp(angle, 12) to_send[0].RDLR = ((t & 0xF00) >> 8) | ((t & 0xFF) << 8) to_send[0].RDLR |= ((state & 0xf) << 4) @@ -72,8 +65,7 @@ def _ipas_control_msg(self, angle, state): return to_send def _speed_msg(self, speed): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0xb4 << 21 + to_send = make_msg(0, 0xB4) speed = int(speed * 100 * 3.6) to_send[0].RDHR = ((speed & 0xFF) << 16) | (speed & 0xFF00) diff --git a/tests/safety/test_volkswagen.py b/tests/safety/test_volkswagen.py index aa535cdac9cbf1..99d0916e46d0a8 100644 --- a/tests/safety/test_volkswagen.py +++ b/tests/safety/test_volkswagen.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import unittest import numpy as np -import libpandasafety_py # pylint: disable=import-error 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 = 4 MAX_RATE_DOWN = 10 @@ -14,6 +15,8 @@ DRIVER_TORQUE_ALLOWANCE = 80 DRIVER_TORQUE_FACTOR = 3 +TX_MSGS = [[0x126, 0], [0x12B, 0], [0x12B, 2], [0x397, 0]] + def sign(a): if a > 0: return 1 @@ -24,24 +27,15 @@ class TestVolkswagenSafety(unittest.TestCase): @classmethod def setUp(cls): cls.safety = libpandasafety_py.libpandasafety - cls.safety.safety_set_mode(Panda.SAFETY_VOLKSWAGEN, 0) + cls.safety.set_safety_hooks(Panda.SAFETY_VOLKSWAGEN, 0) cls.safety.init_tests_volkswagen() - 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): self.safety.set_volkswagen_desired_torque_last(t) self.safety.set_volkswagen_rt_torque_last(t) def _torque_driver_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x9F << 21 - + to_send = make_msg(0, 0x9F) t = abs(torque) to_send[0].RDHR = ((t & 0x1FFF) << 8) if torque < 0: @@ -49,9 +43,7 @@ def _torque_driver_msg(self, torque): return to_send def _torque_msg(self, torque): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x126 << 21 - + to_send = make_msg(0, 0x126) t = abs(torque) to_send[0].RDLR = (t & 0xFFF) << 16 if torque < 0: @@ -59,20 +51,21 @@ def _torque_msg(self, torque): return to_send def _gas_msg(self, gas): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x121 << 21 + to_send = make_msg(0, 0x121) to_send[0].RDLR = (gas & 0xFF) << 12 - return to_send def _button_msg(self, bit): - to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_send[0].RIR = 0x12B << 21 + to_send = make_msg(2, 0x12B) to_send[0].RDLR = 1 << bit - to_send[0].RDTR = 2 << 4 - 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, 0x126) + def test_prev_gas(self): for g in range(0, 256): self.safety.safety_rx_hook(self._gas_msg(g)) @@ -82,18 +75,13 @@ def test_default_controls_not_allowed(self): self.assertFalse(self.safety.get_controls_allowed()) def test_enable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x122 << 21 + to_push = make_msg(0, 0x122) to_push[0].RDHR = 0x30000000 - self.safety.safety_rx_hook(to_push) self.assertTrue(self.safety.get_controls_allowed()) def test_disable_control_allowed_from_cruise(self): - to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - to_push[0].RIR = 0x122 << 21 - to_push[0].RDHR = 0 - + to_push = make_msg(0, 0x122) self.safety.set_controls_allowed(1) self.safety.safety_rx_hook(to_push) self.assertFalse(self.safety.get_controls_allowed()) @@ -130,10 +118,7 @@ def test_steer_safety_check(self): self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_controls_allowed(0) - self.assertFalse(self.safety.get_controls_allowed()) + test_manually_enable_controls_allowed(self) def test_spam_cancel_safety_check(self): BIT_CANCEL = 13 @@ -226,10 +211,10 @@ def test_realtime_limits(self): def test_fwd_hook(self): - buss = list(range(0x0, 0x2)) + buss = list(range(0x0, 0x3)) msgs = list(range(0x1, 0x800)) blocked_msgs_0to2 = [] - blocked_msgs_2to0 = [0x122, 0x397] + blocked_msgs_2to0 = [0x126, 0x397] for b in buss: for m in msgs: if b == 0: @@ -240,7 +225,7 @@ def test_fwd_hook(self): fwd_bus = -1 if m in blocked_msgs_2to0 else 0 # 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__": diff --git a/tests/safety_replay/helpers.py b/tests/safety_replay/helpers.py index 6f4e63c3256304..8fa1d3a1e9ebab 100644 --- a/tests/safety_replay/helpers.py +++ b/tests/safety_replay/helpers.py @@ -59,11 +59,13 @@ def set_desired_torque_last(safety, mode, torque): safety.set_subaru_desired_torque_last(torque) def package_can_msg(msg): - addr_shift = 3 if msg.address >= 0x800 else 21 rdlr, rdhr = struct.unpack('II', msg.dat.ljust(8, b'\x00')) ret = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - ret[0].RIR = msg.address << addr_shift + if msg.address >= 0x800: + ret[0].RIR = (msg.address << 3) | 5 + else: + ret[0].RIR = (msg.address << 21) | 1 ret[0].RDTR = len(msg.dat) | ((msg.src & 0xF) << 4) ret[0].RDHR = rdhr ret[0].RDLR = rdlr diff --git a/tests/safety_replay/replay_drive.py b/tests/safety_replay/replay_drive.py index 3db57dfd168fae..09c677cacb83af 100755 --- a/tests/safety_replay/replay_drive.py +++ b/tests/safety_replay/replay_drive.py @@ -2,7 +2,7 @@ import os import sys -import panda.tests.safety.libpandasafety_py as libpandasafety_py +from panda.tests.safety import libpandasafety_py from panda.tests.safety_replay.helpers import package_can_msg, init_segment from tools.lib.logreader import LogReader # pylint: disable=import-error @@ -10,7 +10,7 @@ def replay_drive(lr, safety_mode, param): safety = libpandasafety_py.libpandasafety - err = safety.safety_set_mode(safety_mode, param) + err = safety.set_safety_hooks(safety_mode, param) assert err == 0, "invalid safety mode: %d" % safety_mode if "SEGMENT" in os.environ: @@ -35,7 +35,7 @@ def replay_drive(lr, safety_mode, param): blocked_addrs.add(canmsg.address) if "DEBUG" in os.environ: - print("blocked %d at %f" % (canmsg.address, (msg.logMonoTime - start_t)/(1e9))) + print("blocked bus %d msg %d at %f" % (canmsg.src, canmsg.address, (msg.logMonoTime - start_t)/(1e9))) tx_controls += safety.get_controls_allowed() tx_tot += 1 elif msg.which() == 'can': diff --git a/tests/safety_replay/test_safety_replay.py b/tests/safety_replay/test_safety_replay.py index cb38a94edff66a..b4278351fee0b3 100755 --- a/tests/safety_replay/test_safety_replay.py +++ b/tests/safety_replay/test_safety_replay.py @@ -16,8 +16,9 @@ ("f89c604cf653e2bf|2018-09-29--13-46-50.bz2", Panda.SAFETY_GM, 0), # GM.VOLT ("0375fdf7b1ce594d|2019-05-21--20-10-33.bz2", Panda.SAFETY_HONDA_BOSCH, 1), # HONDA.ACCORD ("02ec6bea180a4d36|2019-04-17--11-21-35.bz2", Panda.SAFETY_HYUNDAI, 0), # HYUNDAI.SANTA_FE - ("03efb1fda29e30fe|2019-02-21--18-03-45.bz2", Panda.SAFETY_CHRYSLER, 0), # CHRYSLER.PACIFICA_2018_HYBRID + ("6fb4948a7ebe670e|2019-11-12--00-35-53.bz2", Panda.SAFETY_CHRYSLER, 0), # CHRYSLER.PACIFICA_2018_HYBRID ("791340bc01ed993d|2019-04-08--10-26-00.bz2", Panda.SAFETY_SUBARU, 0), # SUBARU.IMPREZA + ("b0c9d2329ad1606b|2019-11-17--17-06-13.bz2", Panda.SAFETY_VOLKSWAGEN, 0), # VOLKSWAGEN.GOLF ] if __name__ == "__main__":