From bfa40539ab10b8fb733f848dfec3d93a3d4b6f19 Mon Sep 17 00:00:00 2001 From: Vehicle Researcher Date: Sat, 16 May 2020 01:15:33 -0700 Subject: [PATCH] Squashed 'panda/' changes from 869f12321..b2c720bf4 b2c720bf4 Dos (#533) 01bf74024 remove 0x1BE checksum test 0bd06c9e0 remove 0x1BE check (breaks some vehicles) c31b899a5 honda bosch longitudinal safety 66250c41d Disable docker layer caching (#534) 6b19fa496 include nissan safety in release build db31886ad gate mazda safety behind debug flag e4558c073 Safety: message length check on RX and TX (#529) git-subtree-dir: panda git-subtree-split: b2c720bf40ef409b27505ae085942ae73f12e2d2 --- .circleci/config.yml | 12 +- board/board.h | 12 +- board/board_declarations.h | 1 + board/boards/dos.h | 224 +++++++++++++++++++++++++++++ board/drivers/llcan.h | 1 + board/safety.h | 18 ++- board/safety/safety_chrysler.h | 16 +-- board/safety/safety_gm.h | 21 ++- board/safety/safety_honda.h | 83 ++++++++--- board/safety/safety_hyundai.h | 15 +- board/safety/safety_nissan.h | 15 +- board/safety/safety_subaru.h | 25 ++-- board/safety/safety_toyota.h | 18 +-- board/safety/safety_volkswagen.h | 28 ++-- board/safety_declarations.h | 8 +- tests/safety/Dockerfile | 2 +- tests/safety/libpandasafety_py.py | 1 + tests/safety/test.c | 5 + tests/safety/test_honda.py | 174 ++++++++++++++++------ tests/safety/test_volkswagen_pq.py | 6 +- 20 files changed, 526 insertions(+), 159 deletions(-) create mode 100644 board/boards/dos.h diff --git a/.circleci/config.yml b/.circleci/config.yml index 52e456066c6290..4229d4565a0084 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: safety: machine: - docker_layer_caching: true + image: ubuntu-1604:202004-01 steps: - checkout - run: @@ -15,7 +15,7 @@ jobs: misra-c2012: machine: - docker_layer_caching: true + image: ubuntu-1604:202004-01 steps: - checkout - run: @@ -35,7 +35,7 @@ jobs: build: machine: - docker_layer_caching: true + image: ubuntu-1604:202004-01 steps: - checkout - run: @@ -68,7 +68,7 @@ jobs: safety_replay: machine: - docker_layer_caching: true + image: ubuntu-1604:202004-01 steps: - checkout - run: @@ -81,7 +81,7 @@ jobs: language_check: machine: - docker_layer_caching: true + image: ubuntu-1604:202004-01 steps: - checkout - run: @@ -94,7 +94,7 @@ jobs: linter_python: machine: - docker_layer_caching: true + image: ubuntu-1604:202004-01 steps: - checkout - run: diff --git a/board/board.h b/board/board.h index 30e1fa4d17a51e..28516299c85d73 100644 --- a/board/board.h +++ b/board/board.h @@ -13,6 +13,7 @@ #include "boards/grey.h" #include "boards/black.h" #include "boards/uno.h" + #include "boards/dos.h" #else #include "boards/pedal.h" #endif @@ -22,7 +23,10 @@ void detect_board_type(void) { // SPI lines floating: white (TODO: is this reliable? Not really, we have to enable ESP/GPS to be able to detect this on the UART) set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 1); - if((detect_with_pull(GPIOA, 4, PULL_DOWN)) || (detect_with_pull(GPIOA, 5, PULL_DOWN)) || (detect_with_pull(GPIOA, 6, PULL_DOWN)) || (detect_with_pull(GPIOA, 7, PULL_DOWN))){ + if(!detect_with_pull(GPIOB, 1, PULL_UP)){ + hw_type = HW_TYPE_DOS; + current_board = &board_dos; + } else 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; } else if(detect_with_pull(GPIOA, 13, PULL_DOWN)) { // Rev AB deprecated, so no pullup means black. In REV C, A13 is pulled up to 5V with a 10K @@ -78,7 +82,7 @@ bool board_has_gmlan(void) { } bool board_has_obd(void) { - return ((hw_type == HW_TYPE_BLACK_PANDA) || (hw_type == HW_TYPE_UNO)); + return ((hw_type == HW_TYPE_BLACK_PANDA) || (hw_type == HW_TYPE_UNO) || (hw_type == HW_TYPE_DOS)); } bool board_has_lin(void) { @@ -86,9 +90,9 @@ bool board_has_lin(void) { } bool board_has_rtc(void) { - return (hw_type == HW_TYPE_UNO); + return ((hw_type == HW_TYPE_UNO) || (hw_type == HW_TYPE_DOS)); } bool board_has_relay(void) { - return ((hw_type == HW_TYPE_BLACK_PANDA) || (hw_type == HW_TYPE_UNO)); + return ((hw_type == HW_TYPE_BLACK_PANDA) || (hw_type == HW_TYPE_UNO) || (hw_type == HW_TYPE_DOS)); } diff --git a/board/board_declarations.h b/board/board_declarations.h index d973551bafc2e0..d5e9e06a8c5991 100644 --- a/board/board_declarations.h +++ b/board/board_declarations.h @@ -39,6 +39,7 @@ struct board { #define HW_TYPE_BLACK_PANDA 3U #define HW_TYPE_PEDAL 4U #define HW_TYPE_UNO 5U +#define HW_TYPE_DOS 6U // LED colors #define LED_RED 0U diff --git a/board/boards/dos.h b/board/boards/dos.h new file mode 100644 index 00000000000000..afccd373da2e7d --- /dev/null +++ b/board/boards/dos.h @@ -0,0 +1,224 @@ +// ///////////// // +// Dos + Harness // +// ///////////// // + +void dos_enable_can_transciever(uint8_t transciever, bool enabled) { + switch (transciever){ + case 1U: + set_gpio_output(GPIOC, 1, !enabled); + break; + case 2U: + set_gpio_output(GPIOC, 13, !enabled); + break; + case 3U: + set_gpio_output(GPIOA, 0, !enabled); + break; + case 4U: + set_gpio_output(GPIOB, 10, !enabled); + break; + default: + puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); + break; + } +} + +void dos_enable_can_transcievers(bool enabled) { + for(uint8_t i=1U; i<=4U; i++){ + // Leave main CAN always on for CAN-based ignition detection + if((car_harness_status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){ + uno_enable_can_transciever(i, true); + } else { + uno_enable_can_transciever(i, enabled); + } + } +} + +void dos_set_led(uint8_t color, bool enabled) { + switch (color){ + case LED_RED: + set_gpio_output(GPIOC, 9, !enabled); + break; + case LED_GREEN: + set_gpio_output(GPIOC, 7, !enabled); + break; + case LED_BLUE: + set_gpio_output(GPIOC, 6, !enabled); + break; + default: + break; + } +} + +void dos_set_gps_load_switch(bool enabled) { + UNUSED(enabled); +} + +void dos_set_bootkick(bool enabled){ + UNUSED(enabled); +} + +void dos_bootkick(void) {} + +void dos_set_phone_power(bool enabled){ + UNUSED(enabled); +} + +void dos_set_usb_power_mode(uint8_t mode) { + UNUSED(mode); +} + +void dos_set_esp_gps_mode(uint8_t mode) { + UNUSED(mode); +} + +void dos_set_can_mode(uint8_t mode){ + switch (mode) { + case CAN_MODE_NORMAL: + case CAN_MODE_OBD_CAN2: + if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(car_harness_status == HARNESS_STATUS_FLIPPED)) { + // B12,B13: disable OBD mode + set_gpio_mode(GPIOB, 12, MODE_INPUT); + set_gpio_mode(GPIOB, 13, MODE_INPUT); + + // B5,B6: normal CAN2 mode + set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); + } else { + // B5,B6: disable normal CAN2 mode + set_gpio_mode(GPIOB, 5, MODE_INPUT); + set_gpio_mode(GPIOB, 6, MODE_INPUT); + + // B12,B13: OBD mode + set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); + } + break; + default: + puts("Tried to set unsupported CAN mode: "); puth(mode); puts("\n"); + break; + } +} + +void dos_usb_power_mode_tick(uint32_t uptime){ + UNUSED(uptime); + if(bootkick_timer != 0U){ + bootkick_timer--; + } else { + dos_set_bootkick(false); + } +} + +bool dos_check_ignition(void){ + // ignition is checked through harness + return harness_check_ignition(); +} + +void dos_set_usb_switch(bool phone){ + set_gpio_output(GPIOB, 3, phone); +} + +void dos_set_ir_power(uint8_t percentage){ + pwm_set(TIM4, 2, percentage); +} + +void dos_set_fan_power(uint8_t percentage){ + // Enable fan power only if percentage is non-zero. + set_gpio_output(GPIOA, 1, (percentage != 0U)); + fan_set_power(percentage); +} + +uint32_t dos_read_current(void){ + // No current sense on Dos + return 0U; +} + +void dos_init(void) { + common_init_gpio(); + + // A8,A15: normal CAN3 mode + set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); + set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); + + // C0: OBD_SBU1 (orientation detection) + // C3: OBD_SBU2 (orientation detection) + set_gpio_mode(GPIOC, 0, MODE_ANALOG); + set_gpio_mode(GPIOC, 3, MODE_ANALOG); + + // C10: OBD_SBU1_RELAY (harness relay driving output) + // C11: OBD_SBU2_RELAY (harness relay driving output) + set_gpio_mode(GPIOC, 10, MODE_OUTPUT); + set_gpio_mode(GPIOC, 11, MODE_OUTPUT); + set_gpio_output_type(GPIOC, 10, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output(GPIOC, 10, 1); + set_gpio_output(GPIOC, 11, 1); + + // C8: FAN PWM aka TIM3_CH3 + set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); + + // Initialize IR PWM and set to 0% + set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4); + pwm_init(TIM4, 2); + dos_set_ir_power(0U); + + // Initialize fan and set to 0% + fan_init(); + dos_set_fan_power(0U); + + // Initialize harness + harness_init(); + + // Initialize RTC + rtc_init(); + + // Enable CAN transcievers + dos_enable_can_transcievers(true); + + // Disable LEDs + dos_set_led(LED_RED, false); + dos_set_led(LED_GREEN, false); + dos_set_led(LED_BLUE, false); + + // Set normal CAN mode + dos_set_can_mode(CAN_MODE_NORMAL); + + // flip CAN0 and CAN2 if we are flipped + if (car_harness_status == HARNESS_STATUS_FLIPPED) { + can_flip_buses(0, 2); + } + + // init multiplexer + can_set_obd(car_harness_status, false); +} + +const harness_configuration dos_harness_config = { + .has_harness = true, + .GPIO_SBU1 = GPIOC, + .GPIO_SBU2 = GPIOC, + .GPIO_relay_SBU1 = GPIOC, + .GPIO_relay_SBU2 = GPIOC, + .pin_SBU1 = 0, + .pin_SBU2 = 3, + .pin_relay_SBU1 = 10, + .pin_relay_SBU2 = 11, + .adc_channel_SBU1 = 10, + .adc_channel_SBU2 = 13 +}; + +const board board_dos = { + .board_type = "Dos", + .harness_config = &dos_harness_config, + .init = dos_init, + .enable_can_transciever = dos_enable_can_transciever, + .enable_can_transcievers = dos_enable_can_transcievers, + .set_led = dos_set_led, + .set_usb_power_mode = dos_set_usb_power_mode, + .set_esp_gps_mode = dos_set_esp_gps_mode, + .set_can_mode = dos_set_can_mode, + .usb_power_mode_tick = dos_usb_power_mode_tick, + .check_ignition = dos_check_ignition, + .read_current = dos_read_current, + .set_fan_power = dos_set_fan_power, + .set_ir_power = dos_set_ir_power, + .set_phone_power = dos_set_phone_power +}; diff --git a/board/drivers/llcan.h b/board/drivers/llcan.h index e467d67bc164ae..0e331526444b17 100644 --- a/board/drivers/llcan.h +++ b/board/drivers/llcan.h @@ -14,6 +14,7 @@ #define GET_BYTE(msg, b) (((int)(b) > 3) ? (((msg)->RDHR >> (8U * ((unsigned int)(b) % 4U))) & 0xFFU) : (((msg)->RDLR >> (8U * (unsigned int)(b))) & 0xFFU)) #define GET_BYTES_04(msg) ((msg)->RDLR) #define GET_BYTES_48(msg) ((msg)->RDHR) +#define GET_FLAG(value, mask) (((__typeof__(mask))param & mask) == mask) #define CAN_INIT_TIMEOUT_MS 500U #define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==CAN1) ? "CAN1" : (((CAN_DEV) == CAN2) ? "CAN2" : "CAN3")) diff --git a/board/safety.h b/board/safety.h index d2d107d66a2772..ebe81f92c55a3a 100644 --- a/board/safety.h +++ b/board/safety.h @@ -72,10 +72,14 @@ void gen_crc_lookup_table(uint8_t poly, uint8_t crc_lut[]) { } } -bool msg_allowed(int addr, int bus, const AddrBus addr_list[], int len) { +bool msg_allowed(CAN_FIFOMailBox_TypeDef *to_send, const CanMsg msg_list[], int len) { + int addr = GET_ADDR(to_send); + int bus = GET_BUS(to_send); + int length = GET_LEN(to_send); + bool allowed = false; for (int i = 0; i < len; i++) { - if ((addr == addr_list[i].addr) && (bus == addr_list[i].bus)) { + if ((addr == msg_list[i].addr) && (bus == msg_list[i].bus) && (length == msg_list[i].len)) { allowed = true; break; } @@ -92,11 +96,13 @@ uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last) { int get_addr_check_index(CAN_FIFOMailBox_TypeDef *to_push, AddrCheckStruct addr_list[], const int len) { int bus = GET_BUS(to_push); int addr = GET_ADDR(to_push); + int length = GET_LEN(to_push); int index = -1; for (int i = 0; i < len; i++) { - for (uint8_t j = 0U; addr_list[i].addr[j] != 0; j++) { - if ((addr == addr_list[i].addr[j]) && (bus == addr_list[i].bus)) { + for (uint8_t j = 0U; addr_list[i].msg[j].addr != 0; j++) { + if ((addr == addr_list[i].msg[j].addr) && (bus == addr_list[i].msg[j].bus) && + (length == addr_list[i].msg[j].len)) { index = i; goto Return; } @@ -209,13 +215,13 @@ const safety_hook_config safety_hook_registry[] = { {SAFETY_CHRYSLER, &chrysler_hooks}, {SAFETY_SUBARU, &subaru_hooks}, {SAFETY_SUBARU_LEGACY, &subaru_legacy_hooks}, - {SAFETY_MAZDA, &mazda_hooks}, {SAFETY_VOLKSWAGEN_MQB, &volkswagen_mqb_hooks}, {SAFETY_VOLKSWAGEN_PQ, &volkswagen_pq_hooks}, + {SAFETY_NISSAN, &nissan_hooks}, {SAFETY_NOOUTPUT, &nooutput_hooks}, #ifdef ALLOW_DEBUG + {SAFETY_MAZDA, &mazda_hooks}, {SAFETY_TESLA, &tesla_hooks}, - {SAFETY_NISSAN, &nissan_hooks}, {SAFETY_ALLOUTPUT, &alloutput_hooks}, {SAFETY_GM_ASCM, &gm_ascm_hooks}, {SAFETY_FORD, &ford_hooks}, diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h index 82fcfb1495d579..59d8af69b5fbbe 100644 --- a/board/safety/safety_chrysler.h +++ b/board/safety/safety_chrysler.h @@ -6,15 +6,14 @@ const int CHRYSLER_MAX_RATE_DOWN = 3; const int CHRYSLER_MAX_TORQUE_ERROR = 80; // max torque cmd in excess of torque motor const int CHRYSLER_GAS_THRSLD = 30; // 7% more than 2m/s const int CHRYSLER_STANDSTILL_THRSLD = 10; // about 1m/s -const AddrBus CHRYSLER_TX_MSGS[] = {{571, 0}, {658, 0}, {678, 0}}; +const CanMsg CHRYSLER_TX_MSGS[] = {{571, 0, 3}, {658, 0, 6}, {678, 0, 8}}; -// TODO: do checksum and counter checks AddrCheckStruct chrysler_rx_checks[] = { - {.addr = {544}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, - {.addr = {514}, .bus = 0, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, - {.addr = {500}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, - {.addr = {308}, .bus = 0, .check_checksum = false, .max_counter = 15U, .expected_timestep = 20000U}, - {.addr = {320}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{544, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, + {.msg = {{514, 0, 8}}, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, + {.msg = {{500, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{308, 0, 8}}, .check_checksum = false, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{320, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, }; const int CHRYSLER_RX_CHECK_LEN = sizeof(chrysler_rx_checks) / sizeof(chrysler_rx_checks[0]); @@ -132,9 +131,8 @@ 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 (!msg_allowed(addr, bus, CHRYSLER_TX_MSGS, sizeof(CHRYSLER_TX_MSGS) / sizeof(CHRYSLER_TX_MSGS[0]))) { + if (!msg_allowed(to_send, CHRYSLER_TX_MSGS, sizeof(CHRYSLER_TX_MSGS) / sizeof(CHRYSLER_TX_MSGS[0]))) { tx = 0; } diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h index 264df31323c2a9..da1acd499e8bae 100644 --- a/board/safety/safety_gm.h +++ b/board/safety/safety_gm.h @@ -18,18 +18,18 @@ 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 +const CanMsg GM_TX_MSGS[] = {{384, 0, 4}, {1033, 0, 7}, {1034, 0, 7}, {715, 0, 8}, {880, 0, 6}, // pt bus + {161, 1, 7}, {774, 1, 8}, {776, 1, 7}, {784, 1, 2}, // obs bus + {789, 2, 5}, // ch bus + {0x104c006c, 3, 3}, {0x10400060, 3, 5}}; // gmlan // TODO: do checksum and counter checks. Add correct timestep, 0.1s for now. AddrCheckStruct gm_rx_checks[] = { - {.addr = {388}, .bus = 0, .expected_timestep = 100000U}, - {.addr = {842}, .bus = 0, .expected_timestep = 100000U}, - {.addr = {481}, .bus = 0, .expected_timestep = 100000U}, - {.addr = {241}, .bus = 0, .expected_timestep = 100000U}, - {.addr = {417}, .bus = 0, .expected_timestep = 100000U}, + {.msg = {{388, 0, 8}}, .expected_timestep = 100000U}, + {.msg = {{842, 0, 5}}, .expected_timestep = 100000U}, + {.msg = {{481, 0, 7}}, .expected_timestep = 100000U}, + {.msg = {{241, 0, 6}}, .expected_timestep = 100000U}, + {.msg = {{417, 0, 7}}, .expected_timestep = 100000U}, }; const int GM_RX_CHECK_LEN = sizeof(gm_rx_checks) / sizeof(gm_rx_checks[0]); @@ -122,9 +122,8 @@ 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 (!msg_allowed(addr, bus, GM_TX_MSGS, sizeof(GM_TX_MSGS)/sizeof(GM_TX_MSGS[0]))) { + if (!msg_allowed(to_send, GM_TX_MSGS, sizeof(GM_TX_MSGS)/sizeof(GM_TX_MSGS[0]))) { tx = 0; } diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h index 9f09da95375e05..3ee1a73d37ff37 100644 --- a/board/safety/safety_honda.h +++ b/board/safety/safety_honda.h @@ -6,9 +6,11 @@ // 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}}; -const AddrBus HONDA_BG_TX_MSGS[] = {{0xE4, 2}, {0xE5, 2}, {0x296, 0}, {0x33D, 2}}; // Bosch Giraffe -const AddrBus HONDA_BH_TX_MSGS[] = {{0xE4, 0}, {0xE5, 0}, {0x296, 1}, {0x33D, 0}}; // Bosch Harness +const CanMsg HONDA_N_TX_MSGS[] = {{0xE4, 0, 5}, {0x194, 0, 4}, {0x1FA, 0, 8}, {0x200, 0, 6}, {0x30C, 0, 8}, {0x33D, 0, 5}}; +const CanMsg HONDA_BG_TX_MSGS[] = {{0xE4, 2, 5}, {0xE5, 2, 8}, {0x296, 0, 4}, {0x33D, 2, 5}}; // Bosch Giraffe +const CanMsg HONDA_BH_TX_MSGS[] = {{0xE4, 0, 5}, {0xE5, 0, 8}, {0x296, 1, 4}, {0x33D, 0, 5}}; // Bosch Harness +const CanMsg HONDA_BG_LONG_TX_MSGS[] = {{0xE4, 0, 5}, {0x1DF, 0, 8}, {0x1EF, 0, 8}, {0x1FA, 0, 8}, {0x30C, 0, 8}, {0x33D, 0, 5}, {0x39F, 0, 8}, {0x18DAB0F1, 0, 8}}; // Bosch Giraffe w/ gas and brakes +const CanMsg HONDA_BH_LONG_TX_MSGS[] = {{0xE4, 1, 5}, {0x1DF, 1, 8}, {0x1EF, 1, 8}, {0x1FA, 1, 8}, {0x30C, 1, 8}, {0x33D, 1, 5}, {0x39F, 1, 8}, {0x18DAB0F1, 1, 8}}; // Bosch Harness w/ gas and brakes // Roughly calculated using the offsets in openpilot +5%: // In openpilot: ((gas1_norm + gas2_norm)/2) > 15 @@ -18,26 +20,33 @@ const AddrBus HONDA_BH_TX_MSGS[] = {{0xE4, 0}, {0xE5, 0}, {0x296, 1}, {0x33D, 0} // In this safety: ((gas1 + (gas2/2))/2) > THRESHOLD const int HONDA_GAS_INTERCEPTOR_THRESHOLD = 344; #define HONDA_GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + ((GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2 ) / 2) // avg between 2 tracks +const int HONDA_BOSCH_NO_GAS_VALUE = -30000; // value sent when not requesting gas +const int HONDA_BOSCH_GAS_MAX = 2000; +const int HONDA_BOSCH_ACCEL_MIN = -350; // max braking == -3.5m/s2 // Nidec and Bosch giraffe have pt on bus 0 AddrCheckStruct honda_rx_checks[] = { - {.addr = {0x1A6, 0x296}, .bus = 0, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, - {.addr = { 0x158}, .bus = 0, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, - {.addr = { 0x17C}, .bus = 0, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {.msg = {{0x1A6, 0, 8}, {0x296, 0, 4}}, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, + {.msg = {{0x158, 0, 8}}, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {.msg = {{0x17C, 0, 8}}, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, }; const int HONDA_RX_CHECKS_LEN = sizeof(honda_rx_checks) / sizeof(honda_rx_checks[0]); // Bosch harness has pt on bus 1 AddrCheckStruct honda_bh_rx_checks[] = { - {.addr = {0x296}, .bus = 1, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, - {.addr = {0x158}, .bus = 1, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, - {.addr = {0x17C}, .bus = 1, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {.msg = {{0x296, 1, 4}}, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, + {.msg = {{0x158, 1, 8}}, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {.msg = {{0x17C, 1, 8}}, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, }; const int HONDA_BH_RX_CHECKS_LEN = sizeof(honda_bh_rx_checks) / sizeof(honda_bh_rx_checks[0]); +const uint16_t HONDA_PARAM_ALT_BRAKE = 1; +const uint16_t HONDA_PARAM_BOSCH_LONG = 2; + int honda_brake = 0; bool honda_alt_brake_msg = false; bool honda_fwd_brake = false; +bool honda_bosch_long = false; enum {HONDA_N_HW, HONDA_BG_HW, HONDA_BH_HW} honda_hw = HONDA_N_HW; @@ -191,12 +200,16 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int addr = GET_ADDR(to_send); int bus = GET_BUS(to_send); - if (honda_hw == HONDA_BG_HW) { - tx = msg_allowed(addr, bus, HONDA_BG_TX_MSGS, sizeof(HONDA_BG_TX_MSGS)/sizeof(HONDA_BG_TX_MSGS[0])); - } else if (honda_hw == HONDA_BH_HW) { - tx = msg_allowed(addr, bus, HONDA_BH_TX_MSGS, sizeof(HONDA_BH_TX_MSGS)/sizeof(HONDA_BH_TX_MSGS[0])); + if ((honda_hw == HONDA_BG_HW) && !honda_bosch_long) { + tx = msg_allowed(to_send, HONDA_BG_TX_MSGS, sizeof(HONDA_BG_TX_MSGS)/sizeof(HONDA_BG_TX_MSGS[0])); + } else if ((honda_hw == HONDA_BG_HW) && honda_bosch_long) { + tx = msg_allowed(to_send, HONDA_BG_LONG_TX_MSGS, sizeof(HONDA_BG_LONG_TX_MSGS)/sizeof(HONDA_BG_LONG_TX_MSGS[0])); + } else if ((honda_hw == HONDA_BH_HW) && !honda_bosch_long) { + tx = msg_allowed(to_send, HONDA_BH_TX_MSGS, sizeof(HONDA_BH_TX_MSGS)/sizeof(HONDA_BH_TX_MSGS[0])); + } else if ((honda_hw == HONDA_BH_HW) && honda_bosch_long) { + tx = msg_allowed(to_send, HONDA_BH_LONG_TX_MSGS, sizeof(HONDA_BH_LONG_TX_MSGS)/sizeof(HONDA_BH_LONG_TX_MSGS[0])); } else { - tx = msg_allowed(addr, bus, HONDA_N_TX_MSGS, sizeof(HONDA_N_TX_MSGS)/sizeof(HONDA_N_TX_MSGS[0])); + tx = msg_allowed(to_send, HONDA_N_TX_MSGS, sizeof(HONDA_N_TX_MSGS)/sizeof(HONDA_N_TX_MSGS[0])); } if (relay_malfunction) { @@ -211,9 +224,10 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { pedal_pressed = pedal_pressed || gas_pressed_prev || (gas_interceptor_prev > HONDA_GAS_INTERCEPTOR_THRESHOLD); } bool current_controls_allowed = controls_allowed && !(pedal_pressed); + int bus_pt = (honda_hw == HONDA_BH_HW)? 1 : 0; - // BRAKE: safety check - if ((addr == 0x1FA) && (bus == 0)) { + // BRAKE: safety check (nidec) + if ((addr == 0x1FA) && (bus == bus_pt)) { honda_brake = (GET_BYTE(to_send, 0) << 2) + ((GET_BYTE(to_send, 1) >> 6) & 0x3); if (!current_controls_allowed) { if (honda_brake != 0) { @@ -228,6 +242,31 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { } } + // BRAKE/GAS: safety check (bosch) + if ((addr == 0x1DF) && (bus == bus_pt)) { + int accel = (GET_BYTE(to_send, 3) << 3) | ((GET_BYTE(to_send, 4) >> 5) & 0x7); + accel = to_signed(accel, 11); + if (!current_controls_allowed) { + if (accel != 0) { + tx = 0; + } + } + if (accel < HONDA_BOSCH_ACCEL_MIN) { + tx = 0; + } + + int gas = (GET_BYTE(to_send, 0) << 8) | GET_BYTE(to_send, 1); + gas = to_signed(gas, 16); + if (!current_controls_allowed) { + if (gas != HONDA_BOSCH_NO_GAS_VALUE) { + tx = 0; + } + } + if (gas > HONDA_BOSCH_GAS_MAX) { + tx = 0; + } + } + // STEER: safety check if ((addr == 0xE4) || (addr == 0x194)) { if (!current_controls_allowed) { @@ -245,7 +284,7 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { } } - // GAS: safety check + // GAS: safety check (interceptor) if (addr == 0x200) { if (!current_controls_allowed) { if (GET_BYTE(to_send, 0) || GET_BYTE(to_send, 1)) { @@ -257,7 +296,6 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { // FORCE CANCEL: safety check only relevant when spamming the cancel button in Bosch HW // ensuring that only the cancel button press is sent (VAL 2) when controls are off. // This avoids unintended engagements while still allowing resume spam - int bus_pt = (honda_hw == HONDA_BH_HW)? 1 : 0; if ((addr == 0x296) && !current_controls_allowed && (bus == bus_pt)) { if (((GET_BYTE(to_send, 0) >> 5) & 0x7) != 2) { tx = 0; @@ -275,6 +313,7 @@ static void honda_nidec_init(int16_t param) { gas_interceptor_detected = 0; honda_hw = HONDA_N_HW; honda_alt_brake_msg = false; + honda_bosch_long = false; } static void honda_bosch_giraffe_init(int16_t param) { @@ -282,7 +321,9 @@ static void honda_bosch_giraffe_init(int16_t param) { relay_malfunction_reset(); honda_hw = HONDA_BG_HW; // Checking for alternate brake override from safety parameter - honda_alt_brake_msg = (param == 1) ? true : false; + honda_alt_brake_msg = GET_FLAG(param, HONDA_PARAM_ALT_BRAKE); + // radar disabled so allow gas/brakes + honda_bosch_long = GET_FLAG(param, HONDA_PARAM_BOSCH_LONG); } static void honda_bosch_harness_init(int16_t param) { @@ -290,7 +331,9 @@ static void honda_bosch_harness_init(int16_t param) { relay_malfunction_reset(); honda_hw = HONDA_BH_HW; // Checking for alternate brake override from safety parameter - honda_alt_brake_msg = (param == 1) ? true : false; + honda_alt_brake_msg = GET_FLAG(param, HONDA_PARAM_ALT_BRAKE); + // radar disabled so allow gas/brakes + honda_bosch_long = GET_FLAG(param, HONDA_PARAM_BOSCH_LONG); } static int honda_nidec_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index 61c3b7e8dc393d..adfd1d44ca1f5d 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -6,15 +6,15 @@ const int HYUNDAI_MAX_RATE_DOWN = 7; const int HYUNDAI_DRIVER_TORQUE_ALLOWANCE = 50; const int HYUNDAI_DRIVER_TORQUE_FACTOR = 2; const int HYUNDAI_STANDSTILL_THRSLD = 30; // ~1kph -const AddrBus HYUNDAI_TX_MSGS[] = {{832, 0}, {1265, 0}, {1157, 0}}; +const CanMsg HYUNDAI_TX_MSGS[] = {{832, 0, 8}, {1265, 0, 4}, {1157, 0, 4}}; // TODO: do checksum checks AddrCheckStruct hyundai_rx_checks[] = { - {.addr = {608}, .bus = 0, .max_counter = 3U, .expected_timestep = 10000U}, - {.addr = {897}, .bus = 0, .max_counter = 255U, .expected_timestep = 10000U}, - {.addr = {902}, .bus = 0, .max_counter = 3U, .expected_timestep = 10000U}, - {.addr = {916}, .bus = 0, .max_counter = 7U, .expected_timestep = 10000U}, - {.addr = {1057}, .bus = 0, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{608, 0, 8}}, .max_counter = 3U, .expected_timestep = 10000U}, + {.msg = {{897, 0, 8}}, .max_counter = 255U, .expected_timestep = 10000U}, + {.msg = {{902, 0, 8}}, .max_counter = 3U, .expected_timestep = 10000U}, + {.msg = {{916, 0, 8}}, .max_counter = 7U, .expected_timestep = 10000U}, + {.msg = {{1057, 0, 8}}, .max_counter = 15U, .expected_timestep = 20000U}, }; const int HYUNDAI_RX_CHECK_LEN = sizeof(hyundai_rx_checks) / sizeof(hyundai_rx_checks[0]); @@ -106,9 +106,8 @@ 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 (!msg_allowed(addr, bus, HYUNDAI_TX_MSGS, sizeof(HYUNDAI_TX_MSGS)/sizeof(HYUNDAI_TX_MSGS[0]))) { + if (!msg_allowed(to_send, HYUNDAI_TX_MSGS, sizeof(HYUNDAI_TX_MSGS)/sizeof(HYUNDAI_TX_MSGS[0]))) { tx = 0; } diff --git a/board/safety/safety_nissan.h b/board/safety/safety_nissan.h index aca99d19b69aa9..5a3e33bbdfa429 100644 --- a/board/safety/safety_nissan.h +++ b/board/safety/safety_nissan.h @@ -11,14 +11,14 @@ const struct lookup_t NISSAN_LOOKUP_ANGLE_RATE_DOWN = { const int NISSAN_DEG_TO_CAN = 100; -const AddrBus NISSAN_TX_MSGS[] = {{0x169, 0}, {0x2b1, 0}, {0x4cc, 0}, {0x20b, 2}, {0x280, 2}}; +const CanMsg NISSAN_TX_MSGS[] = {{0x169, 0, 8}, {0x2b1, 0, 8}, {0x4cc, 0, 8}, {0x20b, 2, 8}, {0x280, 2, 8}}; AddrCheckStruct nissan_rx_checks[] = { - {.addr = {0x2}, .bus = 0, .expected_timestep = 10000U}, // STEER_ANGLE_SENSOR (100Hz) - {.addr = {0x285}, .bus = 0, .expected_timestep = 20000U}, // WHEEL_SPEEDS_REAR (50Hz) - {.addr = {0x30f}, .bus = 2, .expected_timestep = 100000U}, // CRUISE_STATE (10Hz) - {.addr = {0x15c, 0x239}, .bus = 0, .expected_timestep = 20000U}, // GAS_PEDAL (100Hz / 50Hz) - {.addr = {0x454, 0x1cc}, .bus = 0, .expected_timestep = 100000U}, // DOORS_LIGHTS (10Hz) / BRAKE (100Hz) + {.msg = {{0x2, 0, 5}}, .expected_timestep = 10000U}, // STEER_ANGLE_SENSOR (100Hz) + {.msg = {{0x285, 0, 8}}, .expected_timestep = 20000U}, // WHEEL_SPEEDS_REAR (50Hz) + {.msg = {{0x30f, 2, 3}}, .expected_timestep = 100000U}, // CRUISE_STATE (10Hz) + {.msg = {{0x15c, 0, 8}, {0x239, 0, 8}}, .expected_timestep = 20000U}, // GAS_PEDAL (100Hz / 50Hz) + {.msg = {{0x454, 0, 8}, {0x1cc, 0, 8}}, .expected_timestep = 100000U}, // DOORS_LIGHTS (10Hz) / BRAKE (100Hz) }; const int NISSAN_RX_CHECK_LEN = sizeof(nissan_rx_checks) / sizeof(nissan_rx_checks[0]); @@ -119,10 +119,9 @@ static int nissan_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static int nissan_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int tx = 1; int addr = GET_ADDR(to_send); - int bus = GET_BUS(to_send); bool violation = 0; - if (!msg_allowed(addr, bus, NISSAN_TX_MSGS, sizeof(NISSAN_TX_MSGS) / sizeof(NISSAN_TX_MSGS[0]))) { + if (!msg_allowed(to_send, NISSAN_TX_MSGS, sizeof(NISSAN_TX_MSGS) / sizeof(NISSAN_TX_MSGS[0]))) { tx = 0; } diff --git a/board/safety/safety_subaru.h b/board/safety/safety_subaru.h index 17bd699d68ec0f..b663802190cc96 100644 --- a/board/safety/safety_subaru.h +++ b/board/safety/safety_subaru.h @@ -9,23 +9,23 @@ const int SUBARU_DRIVER_TORQUE_ALLOWANCE = 60; const int SUBARU_DRIVER_TORQUE_FACTOR = 10; const int SUBARU_STANDSTILL_THRSLD = 20; // about 1kph -const AddrBus SUBARU_TX_MSGS[] = {{0x122, 0}, {0x221, 0}, {0x322, 0}}; -const AddrBus SUBARU_L_TX_MSGS[] = {{0x164, 0}, {0x221, 0}, {0x322, 0}}; +const CanMsg SUBARU_TX_MSGS[] = {{0x122, 0, 8}, {0x221, 0, 8}, {0x322, 0, 8}}; +const CanMsg SUBARU_L_TX_MSGS[] = {{0x164, 0, 8}, {0x221, 0, 8}, {0x322, 0, 8}}; const int SUBARU_TX_MSGS_LEN = sizeof(SUBARU_TX_MSGS) / sizeof(SUBARU_TX_MSGS[0]); const int SUBARU_L_TX_MSGS_LEN = sizeof(SUBARU_L_TX_MSGS) / sizeof(SUBARU_L_TX_MSGS[0]); AddrCheckStruct subaru_rx_checks[] = { - {.addr = { 0x40}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, - {.addr = {0x119}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, - {.addr = {0x139}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, - {.addr = {0x13a}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, - {.addr = {0x240}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 50000U}, + {.msg = {{ 0x40, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, + {.msg = {{0x119, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{0x139, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{0x13a, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{0x240, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 50000U}, }; // TODO: do checksum and counter checks after adding the signals to the outback dbc file AddrCheckStruct subaru_l_rx_checks[] = { - {.addr = {0x140}, .bus = 0, .expected_timestep = 10000U}, - {.addr = {0x371}, .bus = 0, .expected_timestep = 20000U}, - {.addr = {0x144}, .bus = 0, .expected_timestep = 50000U}, + {.msg = {{0x140, 0, 8}}, .expected_timestep = 10000U}, + {.msg = {{0x371, 0, 8}}, .expected_timestep = 20000U}, + {.msg = {{0x144, 0, 8}}, .expected_timestep = 50000U}, }; const int SUBARU_RX_CHECK_LEN = sizeof(subaru_rx_checks) / sizeof(subaru_rx_checks[0]); const int SUBARU_L_RX_CHECK_LEN = sizeof(subaru_l_rx_checks) / sizeof(subaru_l_rx_checks[0]); @@ -131,10 +131,9 @@ static int subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { 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 ((!msg_allowed(addr, bus, SUBARU_TX_MSGS, SUBARU_TX_MSGS_LEN) && subaru_global) || - (!msg_allowed(addr, bus, SUBARU_L_TX_MSGS, SUBARU_L_TX_MSGS_LEN) && !subaru_global)) { + if ((!msg_allowed(to_send, SUBARU_TX_MSGS, SUBARU_TX_MSGS_LEN) && subaru_global) || + (!msg_allowed(to_send, SUBARU_L_TX_MSGS, SUBARU_L_TX_MSGS_LEN) && !subaru_global)) { tx = 0; } diff --git a/board/safety/safety_toyota.h b/board/safety/safety_toyota.h index f69e4d3c6c0385..8d91504012430c 100644 --- a/board/safety/safety_toyota.h +++ b/board/safety/safety_toyota.h @@ -29,16 +29,16 @@ const int TOYOTA_STANDSTILL_THRSLD = 100; // 1kph const int TOYOTA_GAS_INTERCEPTOR_THRSLD = 845; #define TOYOTA_GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + (GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2) // avg between 2 tracks -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}, {0x750, 0}}; // interceptor + Blindspot monitor +const CanMsg TOYOTA_TX_MSGS[] = {{0x283, 0, 7}, {0x2E6, 0, 8}, {0x2E7, 0, 8}, {0x33E, 0, 7}, {0x344, 0, 8}, {0x365, 0, 7}, {0x366, 0, 7}, {0x4CB, 0, 8}, // DSU bus 0 + {0x128, 1, 6}, {0x141, 1, 4}, {0x160, 1, 8}, {0x161, 1, 7}, {0x470, 1, 4}, // DSU bus 1 + {0x2E4, 0, 5}, {0x411, 0, 8}, {0x412, 0, 8}, {0x343, 0, 8}, {0x1D2, 0, 8}, // LKAS + ACC + {0x200, 0, 6}, {0x750, 0, 8}}; // interceptor + Blindspot monitor AddrCheckStruct toyota_rx_checks[] = { - {.addr = { 0xaa}, .bus = 0, .check_checksum = false, .expected_timestep = 12000U}, - {.addr = {0x260}, .bus = 0, .check_checksum = true, .expected_timestep = 20000U}, - {.addr = {0x1D2}, .bus = 0, .check_checksum = true, .expected_timestep = 30000U}, - {.addr = {0x224, 0x226}, .bus = 0, .check_checksum = false, .expected_timestep = 25000U}, + {.msg = {{ 0xaa, 0, 8}}, .check_checksum = false, .expected_timestep = 12000U}, + {.msg = {{0x260, 0, 8}}, .check_checksum = true, .expected_timestep = 20000U}, + {.msg = {{0x1D2, 0, 8}}, .check_checksum = true, .expected_timestep = 30000U}, + {.msg = {{0x224, 0, 8}, {0x226, 0, 8}}, .check_checksum = false, .expected_timestep = 25000U}, }; const int TOYOTA_RX_CHECKS_LEN = sizeof(toyota_rx_checks) / sizeof(toyota_rx_checks[0]); @@ -153,7 +153,7 @@ static int toyota_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int addr = GET_ADDR(to_send); int bus = GET_BUS(to_send); - if (!msg_allowed(addr, bus, TOYOTA_TX_MSGS, sizeof(TOYOTA_TX_MSGS)/sizeof(TOYOTA_TX_MSGS[0]))) { + if (!msg_allowed(to_send, TOYOTA_TX_MSGS, sizeof(TOYOTA_TX_MSGS)/sizeof(TOYOTA_TX_MSGS[0]))) { tx = 0; } diff --git a/board/safety/safety_volkswagen.h b/board/safety/safety_volkswagen.h index 4063fa7d5e8ea0..a02375dc02812f 100644 --- a/board/safety/safety_volkswagen.h +++ b/board/safety/safety_volkswagen.h @@ -18,15 +18,15 @@ const int VOLKSWAGEN_DRIVER_TORQUE_FACTOR = 3; #define MSG_LDW_02 0x397 // TX by OP, Lane line recognition and text alerts // Transmit of GRA_ACC_01 is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration -const AddrBus VOLKSWAGEN_MQB_TX_MSGS[] = {{MSG_HCA_01, 0}, {MSG_GRA_ACC_01, 0}, {MSG_GRA_ACC_01, 2}, {MSG_LDW_02, 0}}; +const CanMsg VOLKSWAGEN_MQB_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_GRA_ACC_01, 0, 8}, {MSG_GRA_ACC_01, 2, 8}, {MSG_LDW_02, 0, 8}}; const int VOLKSWAGEN_MQB_TX_MSGS_LEN = sizeof(VOLKSWAGEN_MQB_TX_MSGS) / sizeof(VOLKSWAGEN_MQB_TX_MSGS[0]); AddrCheckStruct volkswagen_mqb_rx_checks[] = { - {.addr = {MSG_ESP_19}, .bus = 0, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, - {.addr = {MSG_EPS_01}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, - {.addr = {MSG_ESP_05}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, - {.addr = {MSG_TSK_06}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, - {.addr = {MSG_MOTOR_20}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{MSG_ESP_19, 0, 8}}, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, + {.msg = {{MSG_EPS_01, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, + {.msg = {{MSG_ESP_05, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{MSG_TSK_06, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, + {.msg = {{MSG_MOTOR_20, 0, 8}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, }; const int VOLKSWAGEN_MQB_RX_CHECKS_LEN = sizeof(volkswagen_mqb_rx_checks) / sizeof(volkswagen_mqb_rx_checks[0]); @@ -40,14 +40,14 @@ const int VOLKSWAGEN_MQB_RX_CHECKS_LEN = sizeof(volkswagen_mqb_rx_checks) / size #define MSG_LDW_1 0x5BE // TX by OP, Lane line recognition and text alerts // Transmit of GRA_Neu is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration -const AddrBus VOLKSWAGEN_PQ_TX_MSGS[] = {{MSG_HCA_1, 0}, {MSG_GRA_NEU, 0}, {MSG_GRA_NEU, 2}, {MSG_LDW_1, 0}}; +const CanMsg VOLKSWAGEN_PQ_TX_MSGS[] = {{MSG_HCA_1, 0, 5}, {MSG_GRA_NEU, 0, 4}, {MSG_GRA_NEU, 2, 4}, {MSG_LDW_1, 0, 8}}; const int VOLKSWAGEN_PQ_TX_MSGS_LEN = sizeof(VOLKSWAGEN_PQ_TX_MSGS) / sizeof(VOLKSWAGEN_PQ_TX_MSGS[0]); AddrCheckStruct volkswagen_pq_rx_checks[] = { - {.addr = {MSG_LENKHILFE_3}, .bus = 0, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, - {.addr = {MSG_MOTOR_2}, .bus = 0, .check_checksum = false, .max_counter = 0U, .expected_timestep = 20000U}, - {.addr = {MSG_MOTOR_3}, .bus = 0, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, - {.addr = {MSG_BREMSE_3}, .bus = 0, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, + {.msg = {{MSG_LENKHILFE_3, 0, 6}}, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, + {.msg = {{MSG_MOTOR_2, 0, 8}}, .check_checksum = false, .max_counter = 0U, .expected_timestep = 20000U}, + {.msg = {{MSG_MOTOR_3, 0, 8}}, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, + {.msg = {{MSG_BREMSE_3, 0, 8}}, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, }; const int VOLKSWAGEN_PQ_RX_CHECKS_LEN = sizeof(volkswagen_pq_rx_checks) / sizeof(volkswagen_pq_rx_checks[0]); @@ -311,10 +311,9 @@ static bool volkswagen_steering_check(int desired_torque) { static int volkswagen_mqb_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int addr = GET_ADDR(to_send); - int bus = GET_BUS(to_send); int tx = 1; - if (!msg_allowed(addr, bus, VOLKSWAGEN_MQB_TX_MSGS, VOLKSWAGEN_MQB_TX_MSGS_LEN) || relay_malfunction) { + if (!msg_allowed(to_send, VOLKSWAGEN_MQB_TX_MSGS, VOLKSWAGEN_MQB_TX_MSGS_LEN) || relay_malfunction) { tx = 0; } @@ -348,10 +347,9 @@ static int volkswagen_mqb_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { static int volkswagen_pq_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int addr = GET_ADDR(to_send); - int bus = GET_BUS(to_send); int tx = 1; - if (!msg_allowed(addr, bus, VOLKSWAGEN_PQ_TX_MSGS, VOLKSWAGEN_PQ_TX_MSGS_LEN) || relay_malfunction) { + if (!msg_allowed(to_send, VOLKSWAGEN_PQ_TX_MSGS, VOLKSWAGEN_PQ_TX_MSGS_LEN) || relay_malfunction) { tx = 0; } diff --git a/board/safety_declarations.h b/board/safety_declarations.h index be8be93b78023b..7ce73054e8e4fe 100644 --- a/board/safety_declarations.h +++ b/board/safety_declarations.h @@ -17,13 +17,13 @@ struct lookup_t { typedef struct { int addr; int bus; -} AddrBus; + int len; +} CanMsg; // params and flags about checksum, counter and frequency checks for each monitored address typedef struct { // const params - const int addr[3]; // check either messages (e.g. honda steer). Array MUST terminate with a zero to know its length. - const int bus; // bus where to expect the addr. Temp hack: -1 means skip the bus check + const CanMsg msg[3]; // check either messages (e.g. honda steer). Array MUST terminate with an empty struct to know its length. const bool check_checksum; // true is checksum check is performed const uint8_t max_counter; // maximum value of the counter. 0 means that the counter check is skipped const uint32_t expected_timestep; // expected time between message updates [us] @@ -50,7 +50,7 @@ bool driver_limit_check(int val, int val_last, struct sample_t *val_driver, bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA); float interpolate(struct lookup_t xy, float x); void gen_crc_lookup_table(uint8_t poly, uint8_t crc_lut[]); -bool msg_allowed(int addr, int bus, const AddrBus addr_list[], int len); +bool msg_allowed(CAN_FIFOMailBox_TypeDef *to_send, const CanMsg msg_list[], int len); int get_addr_check_index(CAN_FIFOMailBox_TypeDef *to_push, AddrCheckStruct addr_list[], const int len); void update_counter(AddrCheckStruct addr_list[], int index, uint8_t counter); void update_addr_timestamp(AddrCheckStruct addr_list[], int index); diff --git a/tests/safety/Dockerfile b/tests/safety/Dockerfile index 524cfc3991a071..0bad05417fdc1e 100644 --- a/tests/safety/Dockerfile +++ b/tests/safety/Dockerfile @@ -34,7 +34,7 @@ RUN pip install -r /tmp/requirements.txt WORKDIR /openpilot RUN git clone https://github.com/commaai/opendbc.git || true WORKDIR /openpilot/opendbc -RUN git pull && git checkout 7f3b1774dd248d4ebad91cc9de0fb1c561fab54b +RUN git pull && git checkout f1e69a6cf91cdaf1b8008d73f6fbb6634fbbeb42 WORKDIR /openpilot RUN git clone https://github.com/commaai/cereal.git WORKDIR /openpilot/cereal diff --git a/tests/safety/libpandasafety_py.py b/tests/safety/libpandasafety_py.py index 9898e6df8924b9..00209c668379d9 100644 --- a/tests/safety/libpandasafety_py.py +++ b/tests/safety/libpandasafety_py.py @@ -66,6 +66,7 @@ void init_tests_honda(void); void set_honda_fwd_brake(bool); void set_honda_alt_brake_msg(bool); +void set_honda_bosch_long(bool c); int get_honda_hw(void); void init_tests_chrysler(void); diff --git a/tests/safety/test.c b/tests/safety/test.c index 52d2ea7f1a1080..cf43baa9730cb0 100644 --- a/tests/safety/test.c +++ b/tests/safety/test.c @@ -71,6 +71,7 @@ void fault_recovered(uint32_t fault) { #define GET_BYTE(msg, b) (((int)(b) > 3) ? (((msg)->RDHR >> (8U * ((unsigned int)(b) % 4U))) & 0XFFU) : (((msg)->RDLR >> (8U * (unsigned int)(b))) & 0xFFU)) #define GET_BYTES_04(msg) ((msg)->RDLR) #define GET_BYTES_48(msg) ((msg)->RDHR) +#define GET_FLAG(value, mask) (((__typeof__(mask))param & mask) == mask) #define UNUSED(x) (void)(x) @@ -183,6 +184,10 @@ void set_honda_alt_brake_msg(bool c){ honda_alt_brake_msg = c; } +void set_honda_bosch_long(bool c){ + honda_bosch_long = c; +} + int get_honda_hw(void) { return honda_hw; } diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py index 5cd2ba05f2cdd2..e053e4e586922f 100755 --- a/tests/safety/test_honda.py +++ b/tests/safety/test_honda.py @@ -7,8 +7,6 @@ from panda.tests.safety.common import CANPackerPanda, make_msg, \ MAX_WRONG_COUNTERS, UNSAFE_MODE -MAX_BRAKE = 255 - class Btn: CANCEL = 2 SET = 3 @@ -20,11 +18,14 @@ class Btn: class TestHondaSafety(common.PandaSafetyTest): + MAX_BRAKE = 255 + PT_BUS = None # must be set when inherited + STEER_BUS = None # must be set when inherited + cnt_speed = 0 cnt_gas = 0 cnt_button = 0 - - PT_BUS = 0 + cnt_brake = 0 @classmethod def setUpClass(cls): @@ -58,15 +59,13 @@ def _gas_msg(self, gas): self.__class__.cnt_gas += 1 return self.packer.make_can_msg_panda("POWERTRAIN_DATA", self.PT_BUS, values) - def _send_brake_msg(self, brake): - values = {} - if self.safety.get_honda_hw() == HONDA_N_HW: - values = {"COMPUTER_BRAKE": brake} - return self.packer.make_can_msg_panda("BRAKE_COMMAND", 0, values) - def _send_steer_msg(self, steer): values = {"STEER_TORQUE": steer} - return self.packer.make_can_msg_panda("STEERING_CONTROL", 0, values) + return self.packer.make_can_msg_panda("STEERING_CONTROL", self.STEER_BUS, values) + + def _send_brake_msg(self, brake): + # must be implemented when inherited + raise NotImplementedError() def test_resume_button(self): self.safety.set_controls_allowed(0) @@ -157,13 +156,14 @@ def test_tx_hook_on_pedal_pressed(self): hw = self.safety.get_honda_hw() if hw == HONDA_N_HW: self.safety.set_honda_fwd_brake(False) - self.assertEqual(allow_ctrl, self._tx(self._send_brake_msg(MAX_BRAKE))) + self.assertEqual(allow_ctrl, self._tx(self._send_brake_msg(self.MAX_BRAKE))) self.assertEqual(allow_ctrl, self._tx(self._send_steer_msg(0x1000))) # reset status self.safety.set_controls_allowed(0) self.safety.set_unsafe_mode(UNSAFE_MODE.DEFAULT) - self._tx(self._send_brake_msg(0)) + if hw == HONDA_N_HW: + self._tx(self._send_brake_msg(0)) self._tx(self._send_steer_msg(0)) if pedal == 'brake': self._rx(self._speed_msg(0)) @@ -181,6 +181,9 @@ class TestHondaNidecSafety(TestHondaSafety, common.InterceptorSafetyTest): FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x194, 0x33D, 0x30C]} FWD_BUS_LOOKUP = {0: 2, 2: 0} + PT_BUS = 0 + STEER_BUS = 0 + INTERCEPTOR_THRESHOLD = 344 def setUp(self): @@ -197,6 +200,10 @@ def _interceptor_msg(self, gas, addr): ((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8) return to_send + def _send_brake_msg(self, brake): + values = {"COMPUTER_BRAKE": brake} + return self.packer.make_can_msg_panda("BRAKE_COMMAND", 0, values) + def test_fwd_hook(self): # normal operation, not forwarding AEB self.FWD_BLACKLISTED_ADDRS[2].append(0x1FA) @@ -212,13 +219,13 @@ def test_fwd_hook(self): def test_brake_safety_check(self): for fwd_brake in [False, True]: self.safety.set_honda_fwd_brake(fwd_brake) - for brake in np.arange(0, MAX_BRAKE + 10, 1): + for brake in np.arange(0, self.MAX_BRAKE + 10, 1): for controls_allowed in [True, False]: self.safety.set_controls_allowed(controls_allowed) if fwd_brake: send = False # block openpilot brake msg when fwd'ing stock msg elif controls_allowed: - send = MAX_BRAKE >= brake >= 0 + send = self.MAX_BRAKE >= brake >= 0 else: send = brake == 0 self.assertEqual(send, self._tx(self._send_brake_msg(brake))) @@ -234,7 +241,7 @@ def test_tx_hook_on_interceptor_pressed(self): self.safety.set_controls_allowed(1) self.safety.set_honda_fwd_brake(False) - self.assertEqual(allow_ctrl, self._tx(self._send_brake_msg(MAX_BRAKE))) + self.assertEqual(allow_ctrl, self._tx(self._send_brake_msg(self.MAX_BRAKE))) self.assertEqual(allow_ctrl, self._tx(self._interceptor_msg(self.INTERCEPTOR_THRESHOLD, 0x200))) self.assertEqual(allow_ctrl, self._tx(self._send_steer_msg(0x1000))) @@ -247,27 +254,62 @@ def test_tx_hook_on_interceptor_pressed(self): self.safety.set_gas_interceptor_detected(False) -class TestHondaBoschHarnessSafety(TestHondaSafety): - TX_MSGS = [[0xE4, 0], [0xE5, 0], [0x296, 1], [0x33D, 0]] # Bosch Harness +class TestHondaBoschSafety(TestHondaSafety): STANDSTILL_THRESHOLD = 0 RELAY_MALFUNCTION_ADDR = 0xE4 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestHondaBoschSafety": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def setUp(self): + self.packer = CANPackerPanda("honda_accord_s2t_2018_can_generated") + self.safety = libpandasafety_py.libpandasafety + + def _alt_brake_msg(self, brake): + values = {"BRAKE_PRESSED": brake, "COUNTER": self.cnt_brake % 4} + self.__class__.cnt_brake += 1 + return self.packer.make_can_msg_panda("BRAKE_MODULE", self.PT_BUS, values) + + # TODO: add back in once alternative brake checksum/counter validation is added + # def test_alt_brake_rx_hook(self): + # self.safety.set_honda_alt_brake_msg(1) + # self.safety.set_controls_allowed(1) + # to_push = self._alt_brake_msg(0) + # self.assertTrue(self._rx(to_push)) + # to_push[0].RDLR = to_push[0].RDLR & 0xFFF0FFFF # invalidate checksum + # self.assertFalse(self._rx(to_push)) + # self.assertFalse(self.safety.get_controls_allowed()) + + def test_alt_disengage_on_brake(self): + self.safety.set_honda_alt_brake_msg(1) + self.safety.set_controls_allowed(1) + self._rx(self._alt_brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + + self.safety.set_honda_alt_brake_msg(0) + self.safety.set_controls_allowed(1) + self._rx(self._alt_brake_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + + +class TestHondaBoschHarnessSafety(TestHondaBoschSafety): + TX_MSGS = [[0xE4, 0], [0xE5, 0], [0x296, 1], [0x33D, 0]] # Bosch Harness RELAY_MALFUNCTION_BUS = 0 FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D]} FWD_BUS_LOOKUP = {0: 2, 2: 0} PT_BUS = 1 + STEER_BUS = 0 def setUp(self): - self.packer = CANPackerPanda("honda_accord_s2t_2018_can_generated") - self.safety = libpandasafety_py.libpandasafety + super().setUp() self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_HARNESS, 0) self.safety.init_tests_honda() - def _alt_brake_msg(self, brake): - to_send = make_msg(0, 0x1BE) - to_send[0].RDLR = 0x10 if brake else 0 - return to_send - def test_spam_cancel_safety_check(self): self.safety.set_controls_allowed(0) self.assertTrue(self._tx(self._button_msg(Btn.CANCEL))) @@ -277,37 +319,85 @@ def test_spam_cancel_safety_check(self): self.safety.set_controls_allowed(1) self.assertTrue(self._tx(self._button_msg(Btn.RESUME))) - def test_alt_disengage_on_brake(self): - self.safety.set_honda_alt_brake_msg(1) - self.safety.set_controls_allowed(1) - self._rx(self._alt_brake_msg(1)) - self.assertFalse(self.safety.get_controls_allowed()) - - self.safety.set_honda_alt_brake_msg(0) - self.safety.set_controls_allowed(1) - self._rx(self._alt_brake_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - class TestHondaBoschGiraffeSafety(TestHondaBoschHarnessSafety): TX_MSGS = [[0xE4, 2], [0xE5, 2], [0x296, 0], [0x33D, 2]] # Bosch Giraffe - STANDSTILL_THRESHOLD = 0 - RELAY_MALFUNCTION_ADDR = 0xE4 RELAY_MALFUNCTION_BUS = 2 FWD_BLACKLISTED_ADDRS = {1: [0xE4, 0xE5, 0x33D]} FWD_BUS_LOOKUP = {1: 2, 2: 1} PT_BUS = 0 + STEER_BUS = 2 def setUp(self): super().setUp() - self.safety = libpandasafety_py.libpandasafety self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_GIRAFFE, 0) self.safety.init_tests_honda() - def _send_steer_msg(self, steer): - values = {"STEER_TORQUE": steer} - return self.packer.make_can_msg_panda("STEERING_CONTROL", 2, values) + +class TestHondaBoschLongSafety(TestHondaBoschSafety): + NO_GAS = -30000 + MAX_GAS = 2000 + MAX_BRAKE = -3.5 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestHondaBoschLongSafety": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def _send_gas_brake_msg(self, gas, accel): + values = { + "GAS_COMMAND": gas, + "ACCEL_COMMAND": accel, + "BRAKE_REQUEST": accel < 0, + } + return self.packer.make_can_msg_panda("ACC_CONTROL", self.PT_BUS, values) + + def test_gas_safety_check(self): + for controls_allowed in [True, False]: + for gas in np.arange(self.NO_GAS, self.MAX_GAS + 2000, 100): + accel = 0 if gas < 0 else gas / 1000 + self.safety.set_controls_allowed(controls_allowed) + send = gas <= self.MAX_GAS if controls_allowed else gas == self.NO_GAS + self.assertEqual(send, self.safety.safety_tx_hook(self._send_gas_brake_msg(gas, accel)), gas) + + def test_brake_safety_check(self): + for controls_allowed in [True, False]: + for accel in np.arange(0, self.MAX_BRAKE - 1, -0.1): + self.safety.set_controls_allowed(controls_allowed) + send = self.MAX_BRAKE <= accel <= 0 if controls_allowed else accel == 0 + self.assertEqual(send, self._tx(self._send_gas_brake_msg(self.NO_GAS, accel)), (controls_allowed, accel)) + +class TestHondaBoschLongHarnessSafety(TestHondaBoschLongSafety): + TX_MSGS = [[0xE4, 1], [0x1DF, 1], [0x1EF, 1], [0x1FA, 1], [0x30C, 1], [0x33D, 1], [0x39F, 1], [0x18DAB0F1, 1]] # Bosch Harness w/ gas and brakes + RELAY_MALFUNCTION_BUS = 0 + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + PT_BUS = 1 + STEER_BUS = 1 + + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_HARNESS, 2) + self.safety.init_tests_honda() + + +class TestHondaBoschLongGiraffeSafety(TestHondaBoschLongSafety): + TX_MSGS = [[0xE4, 0], [0x1DF, 0], [0x1EF, 0], [0x1FA, 0], [0x30C, 0], [0x33D, 0], [0x39F, 0], [0x18DAB0F1, 0]] # Bosch Giraffe w/ gas and brakes + RELAY_MALFUNCTION_BUS = 2 + FWD_BLACKLISTED_ADDRS = {1: [0xE4, 0xE5, 0x33D]} + FWD_BUS_LOOKUP = {1: 2, 2: 1} + + PT_BUS = 0 + STEER_BUS = 0 + + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_GIRAFFE, 2) + self.safety.init_tests_honda() if __name__ == "__main__": diff --git a/tests/safety/test_volkswagen_pq.py b/tests/safety/test_volkswagen_pq.py index f30d19f3f82af2..11310e025e1c61 100644 --- a/tests/safety/test_volkswagen_pq.py +++ b/tests/safety/test_volkswagen_pq.py @@ -82,7 +82,7 @@ def _pcm_status_msg(self, cruise): # Driver steering input torque def _lenkhilfe_3_msg(self, torque): - to_send = make_msg(0, MSG_LENKHILFE_3) + to_send = make_msg(0, MSG_LENKHILFE_3, 6) t = abs(torque) to_send[0].RDLR = ((t & 0x3FF) << 16) if torque < 0: @@ -94,7 +94,7 @@ def _lenkhilfe_3_msg(self, torque): # openpilot steering output torque def _hca_1_msg(self, torque): - to_send = make_msg(0, MSG_HCA_1) + to_send = make_msg(0, MSG_HCA_1, 5) t = abs(torque) << 5 # DBC scale from centi-Nm to PQ network (approximated) to_send[0].RDLR = (t & 0x7FFF) << 16 if torque < 0: @@ -120,7 +120,7 @@ def _gas_msg(self, gas): # Cruise control buttons def _gra_neu_msg(self, bit): - to_send = make_msg(2, MSG_GRA_NEU) + to_send = make_msg(2, MSG_GRA_NEU, 4) to_send[0].RDLR = 1 << bit to_send[0].RDLR |= volkswagen_pq_checksum(to_send[0], MSG_GRA_NEU, 8) return to_send